Lang features Flashcards
What is the prelude?
A set of types that Rust brings into scope by default.
References
Todo, add information on how they work.
- References are immutable
- References are safe (see soon)
- Passing in a mutable reference requires you to prefix it with ‘mut’. e.g. .read_line(&mut guess)
Binary crates vs library crates
Binary: meant to run
Library: meant to be used by other code
Overriding of types of an existing variable!
let mut guess = String::new();
let guess: u32 = guess.trim().parse().expect(“Please type a number!”)
No longer..
let mut guessString
let guessNumber = …
AWESOME ! :)
What are awesome features?
- Overriding of types of existing variables or shadowing (let guess.. then let guess:u32)
- Match statement with pattern matching on Ordering for comparison
- Very powerful type inference.. e.g. the case of functions like .parse() based on target type -> let guess: u32 = guess.trim().parse()…. it infers which generic method it has to call, based on the target type. Avoiding the need to specify type parameters of a function.
- Borrowing (to be read more)
- if the last part of a function is an expression, it becomes the return value of the function, if it’s a statement we get an appropriate compiler error.
- IF is an expression. just one way to do things instead of dotnet with their confusing alternate syntaxes for statements versus expressions where some are not allowed in certain contexts which are hard to identify.
- Errors seem very user friendly
- The macro system.
When to use usize?
When holding an index into a data structure,
usize gives you the guarantee to be always big enough to hold any pointer or any offset in a data structure, while u32 can be too small on some architectures.
Statements vs expressions
Expressions evaluate to a value, statements do not.
Expressions DO NOT include semicolons, an expression with a semicolon, becomes a statement.
Macros are expressions.
E.g. let y = {
let x = 3;
x+1
}
The block is an expression
OWNERSHIP, borrowing, slices and data in memory.
ownership rules:
- each value in rust has a variable that’s called it’s owner
- there can only be one owner at a given time.
- when the owner goes out of scope, the value is dropped.
When assigning a reference to another variable, that new variable becomes owner of that reference.
Therefore the old variable is on longer valid!
let s1 = String::from("hello"); let s2 = s1;
println!(“{}, world!”, s1)
error: let s1 = String::from("hello"); | -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait | let s2 = s1; | -- value moved here | | println!("{}, world!", s1) | ^^ value borrowed here after move
Rust will never automatically deep copy your data.
If we do want to deeply copy the heap data, we can use clone.
(or implement the Copy trait????) If a type has the copy trait, old vars\/ remain valid after assignment to a new var.
There is also a Drop trait for types that need cleanup. A type that has the drop trait can’t be annotated with the copy trait.
Passing a value to a function has the same semantics as assignment, the function takes ownership.
fn main() { let s1 = String::from("hello");
takes_ownership(s1);
// println!("{}, world!", s1) }
fn takes_ownership(some_string: String) { println!("{}", some_string); }
Using references is Rust's way of 'borrowing' values. fn fun(s: &String){
}
will automatically be a ‘borrowed’ value. Borrowed values can’t be edited! References are immutable for their values. We do not allow editing values of a immutable reference. However, we can make it mutable:
fn fun(s: &mut String){ mut.push_str(" world"); }
Another limitation, you can only have ONE mutable reference to a particular piece of data in a particular scope. This is used to avoid parallelism issues caused by data races. Throw curly braces around it to create a scope if you need multiple mutable references.
Rust features a lot of “cheating” - pointers that allow mutation from multiple threads, reference-counted pointers, etc. - so obviously just ownership&borrowing isn’t good enough
Stack & Heap
stack stores values in order and returns them in opposite order.. ne stack quoi.. All data
on the stack has a FIXED KNOWN SIZE!
The heap stores data with an UNKNOWN SIZE.
Slices
Allow you to reference a contiguous sequence of elements in a collection rather than the whole collection.
let str = String::from(“Hey hallo eerste woord”);
let hello = &str[3..8];
string literals are by default are string slices.
Partial moves
Within the destructuring of a single variable, both by-move and by-reference pattern bindings can be used at the same time. Doing this will result in a partial move of the variable, which means that parts of the variable will be moved while other parts stay. In such a case, the parent variable cannot be used afterwards as a whole, however the parts that are only referenced (and not moved) can still be used.
Lifetime elision
Implicit lifetime annotations, automatic inference of lifetime by rust.