Learn Rust
Content:
- Basic Types & Variables
- Control Flow
- References, Ownership, and Borrowing
- Pattern Matching
- Iterators
- Error Handling
- Combinators
- Multiple error types
- Iterating over errors
- Generics, Traits, and Lifetimes
- Functions, Function Pointers & Closures
- Pointers
- Smart pointers
- Packages, Crates, and Modules
1. Basic Types & Variables
Boolean
bool
Unsigned integers
u8, u16, u32, u64, u128
Signed integers
i8, i16, i32, i64, i128
Floating point numbers
f32, f64
Platform specific integers
usize - Unsigned integer. Same number of bits as the platform's pointer type.
isize - Signed integer. Same number of bits as the platform's pointer type.
char - Unicode scalar value
&str - String slice
String - Owned string
Tuple
Array & Slice
// Arrays must have a known length and all elements must be initialized
let array = [1, 2, 3, 4, 5];
let array2 = [0; 3]; // [0, 0, 0]
// Unlike arrays the length of a slice is determined at runtime
let slice = &array[1 .. 3];
HashMap
use std::collections::HashMap;
let mut subs = HashMap::new();
subs.insert(String::from("foo"), 100000);
// Insert key if it doesn't have a value
subs.entry("foo".to_owned()).or_insert(3)
Struct
// Definition
struct User {
username: String,
active: bool,
}
// Instantiation
let user1 = User {
username: String::from("foo"),
active: true,
};
// Tuple struct
struct Color(i32, i32, i32);
let black = Color(0, 0, 0);
Enum
// Definition
enum Command {
Quit,
Move { x: i32, y: i32 },
Speak(String),
ChangeBGColor(i32, i32, i32),
}
// Instantiation
let msg1 = Command::Quit;
let msg2 = Command::Move{ x: 1, y: 2 };
let msg3 = Command::Speak("Hi".to_owned());
let msg4 = Command::ChangeBGColor(0, 0, 0);
Constant
Static variable
// Unlike constants static variables are
// stored in a dedicated memory location
// and can be mutated.
static MAJOR_VERSION: u32 = 1;
static mut COUNTER: u32 = 0;
Mutability
Shadowing
Type alias
2. Control Flow
if and if let
let num = Some(22);
if num.is_some() {
println!("number is: {}", num.unwrap());
}
// match pattern and assign variable
if let Some(i) = num {
println!("number is: {}", i);
}
loop
Nested loops & labels
'outer: loop {
'inner: loop {
// This breaks the inner loop
break;
// This breaks the outer loop
break 'outer;
}
}
Returning from loops
while and while let
while n < 101 {
n += 1;
}
let mut optional = Some(0);
while let Some(i) = optional {
print!("{}", i);
}
for loop
for n in 1..101 {
println!("{}", n);
}
let names = vec!["Bogdan", "Wallace"];
for name in names.iter() {
println!("{}", name);
}
match
let optional = Some(0);
match optional {
Some(i) => println!("{}", i),
None => println!("No value.")
}
3. References, Ownership, and Borrowing
Ownership rules
- Each value in Rust has a variable that’s called its owner.
- There can only be one owner at a time.
- When the owner goes out of scope, the value will be dropped.
Borrowing rules
- At any given time, you can have either one mutable reference or any number of immutable references.
- References must always be valid.
Creating references
let s1 = String::from("hello world!");
let s1_ref = &s1; // immutable reference
let mut s2 = String::from("hello");
let s2_ref = &mut s2; // mutable reference
s2_ref.push_str(" world!");
Copy, Move, and Clone
// Simple values which implement the Copy trait are copied by value
let x = 5;
let y = x;
println!("{}", x); // x is still valid
// The string is moved to s2 and s1 is invalidated
let s1 = String::from("Let's Get Rusty!");
let s2 = s1; // Shallow copy a.k.a move
println!("{}", s1); // Error: s1 is invalid
let s1 = String::from("Let's Get Rusty!");
let s2 = s1.clone(); // Deep copy
// Valid because s1 isn't moved
println!("{}", s1);
Ownership and functions
fn main() {
let x = 5;
takes_copy(x); // x is copied by value
let s = String::from("Let’s Get Rusty!");
// s is moved into the function
takes_ownership(s);
// return value is moved into s1
let s1 = gives_ownership();
let s2 = String::from("LGR");
let s3 = takes_and_gives_back(s2);
}
fn takes_copy(some_integer: i32) {
println!("{}", some_integer);
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
} // some_string goes out of scope and drop is called. The backing memory is freed.
fn gives_ownership() -> String {
let some_string = String::from("LGR");
some_string
}
fn takes_and_gives_back(some_string: String) -> String {
some_string
}