My notes and code for working through the Rust book 🦀
fn main()is where execution starts in all Rust programs!means using a macro- Cargo is Rust's build system and package manager
cargo newcargo buildcargo runcargo checkcargo build --release
usefor std and rand lib (an external crate)letmatchloopread_lineexpecttrimparseexpectreturns value in brackets ifResultis anErrvalue, ifResultis anOkvalue, it will return the valueOkis holding
constand type must be annotated!- "Shadowing" or reusing immutable variables by defining
leton the same variable name after it is defined, the second variable overshadows the first until it itself is overshadowed or the scope ends. Shadowing also lets use reuse the variable name but change the type and retain immutability after the transformation.
- Integers
i32u32, etc - Floating points
f64f32 - Numeric operations, remainder
% - Boolean type
truefalse - Character type
chardefined using single quoteslet r = '🦀' - Compound types: tuples and arrays can group multiple values into one type
- Use pattern matching on a tuple to destructure it (get all the individual values into their own variables)
- Access the first element of a tuple, x, by using
x.0 - The tuple without any values () has a special name, "unit". Expressions implicitly return the unit value if they don't return any other value
- Array elements must be of the same type
- Arrays in Rust have a fixed length
- Arrays are useful when you want your data allocated on the stack rather than the heap, or when you want to ensure you will always have a fixed number of elements
- A vector, is a similar type to an array provided by the standard library, but can grow and shrink
- You write an array’s type using square brackets with the type of each element, a semicolon, and then the number of elements in the array, like so:
let a: [i32; 5] = [1, 2, 3, 4, 5]; - Shorthand trick
let a = [3; 5];defines[3, 3, 3, 3, 3] - Access elements in an array using indexing
a[0] - If you use user input for specifying which element to pick and choose an out of bound ones Rust will panick with a run time error, rather than let you continue like in some other low-level languages that will let you access invalid memory. If you try to access out of bounds index without ambigious user input, Rust will catch this and fail during the compilation stage.
mainfunction, entry point of many programsfnkeyword- snake case for function and variable names is the conventional style
- You must declare the type of every parameter in function definition
- Function bodies contain a series of statements optionally ending in an expression. Rust is an expression based language. A statement is for example
let y = 6;. An expression is3 * 2or even just6in the statementlet y = 6;. Expressions don't include ending semicolons (the semicolon in the previous example is a semicolon for the statement not for the6expression). - Function returns
->must specify type. You can return implicitly with the final expression in the block of the body of a function returnkeyword with a value
ifstatement, needs a bool, will not try and convert non bool to bool, like JavaScript does, for example.elseelse ifmatchis recommended over lots ofelse ifs- returning a value after a
break breakandcontinueapply by default to the innermost loop, however you can use loop labels so it can apply to the labelled loop. Loop labels are defined with a single quote and name, like so:'counting_up: loop {}whilefor- Range
(1..4) rev()to reverse the range
- Ownership is a set of rules that govern how a Rust program manages memory. If any of the rules are violated, the program won't compile.
- Main purpose of ownership is to manage heap data (as opposed to stack data)
- Ownership rules: Each value in Rust has an owner, there can only be one owner at a time, when the owner goes out of scope the value will be dropped
- String datatype lives on the heap, and so good example for explaining ownership in Rust. Also note,
Stringis different tostringliterals. There are two different types of string in Rust. - We can't allocate a dynamic piece of memory into the binary we compile, so it must allocated on the heap at runtime. We need a way of returning this mmeory to the allocator when we're done with our
String. - Rust takes a different approach to garbage collectors and letting programmers free and allocate, which is error-prone. The memory is automatically returned once the variable that owns it goes out of scope.
- Rust calls
drop(a special function) behind the scenes - Deep copies vs shallow copies. Because Rust also invalidates the first variable it is called a
moveinstead of shallow copy. clone()arbitrary code (however it is implemented) is being executed and it could be expensive- Types stored on stack, we can implement the
Copytrait. This means variables won'tmove, but are copied. Making them valid after assignment to another variable (unlike heap allocated types) - When sending a variable (which could be heap or stack based) to a function, just like assinging to variables
moveorcopyis called. So once you pass a variable to a function and it getsmoved then you cannot reference that variable after the call to the function.
- Rather than returning a tuple with the value you pass in so it is not
droped we can use a reference&