Udemy Rust Course
Set Up
Install rust (check installation byrustc --version
)
Install c++ build tools to avoid link.exe not found error
Intro
Safety, Concurrency, Speed Cargo - package manager, build system, docs generator Configs files use toml format Command for creating new project (called hello in this case)cargo new hello
Variables
- Typed
- Immutable by default
- Scoped
- Compile-time checks that ensure memory safety as well
const c = 3;
let immutable = 5;
let mut mutable = 7;
Explain error types
rustc --explain E0384
Functions
declared with fn tail expression - without ; it becomes the return valueModules
src/main.rs main file, while src/lib.rs is the main lib file pub makes fn public use statement shortens namespace usage standard library e.g.use std::collections::HashMap
crates.io - rust community registry
packages (or crates) can be added to the dependencies name = "version"
format
Literal Types
u8 to u128 and i8 to i128, usize and isize, f32 and f64 byte usually means u8 underscore are ignored on number representations. e.g. 0xff_32_00_af suffix literal instead of annotating type. E.g. 5u16, 3.14f32 (or 3.14_f32) bool: true, false char - 4 bytes strings are UTF8 internally, sometimes when dealing with a single character, 1 character stringCompound Types
tuples (maximum arity of 12) let t = (1, 3.3, "asd");
let f = t.0;
let g = t.1;
let h = t.2;
let (f,g,h) = t; // access all at once
array (maximum size of 32)
let buf = [1, 2, 3];
let buf = [0; 3]; // [0, 0, 0]
annotation
let buf : [u8; 3] = [0, 0, 0]; // even when you specify all
> than 32, use vector instead
Control flow
If statement - pretty similar, no parenthesis. No auto type conversion, must always eval to bool. if statement can return a value e.g.
let msg = if num==5 {
"five"
} else if num == 3 {
"three"
} else {
"other"
};
note semicolon usage indicating return (regular return won't work for this purpose)
loops can have identifiers (tick) and can be used to break out of it e.g.
'asd : loop { // unconditional loop
loop {
loop {
break 'asd; // also continue
}
}
}
Strings
6 types of string in Rust 2 most used - borrowed string slice (str or &str) string literals are always borrowed string can't be modified - String (created using to_string();) or String::from("literal"); has capacity which can differ from string actual size can be modified access string by index -> complicated due to how unicode works one grapheme can be made out of multiple scalars (e.g. diacritic symbols) using word.bytes can get an addressable array of bytes (works fine for simple ascii text) package unicode-segmentations to handle graphemes nth method to index strings instead of using direct indexOwnership
Only one variable can own a value. It can be borrowed or copied. Variable goes out of scope -> Value gets trashed. Copy using .clone() method. "copy" means only stack data is copied. Clone copy heap and offset ptr to match new variable (deep copy). Passing ownership to functions and returning value means the function intends to modify the passed in value.do_something(&ref_value)
It's possible to make a mutable reference to access mutable values
&mut variable_or_type
dereferencing:
x: &mut i32;
&x // mutable i32
Not possible to have multiple mutable references -> Thread safety
Struct
No classes, no inheritance. Only structs.Traits
Similar to interfaces Traits can implement behaviours for existing types e.g. Copy trait -> object gets copied instead of moved. Good for small objects that fit in the stack. More like "components"Collections
Most common: Vectorlet mut v: Vec<i32> = Vec::new()
macro for creating vector: let mut v: vec![2,4,6]
Also: HashMap, VecDeque, BTreeMap, LinkedList, BinaryHeap, BTreeSet (BT are binary-tree sorted versions)
Enums
Similar to Union in C++ Can store one type of value, and also it's value exampleenum Option<T>{Some(T), None}
if let - checks for one pattern
match - Checks multiple patterns
enum Result<T,E>{OK(T), Err(E)}
Match can use guards for checking for specific values
Closures
syntax islet sum = | x, y | { x + y }
then you call it with sum(2, 3)
Closures can own references and make sure variables live as long as the closure itself survives.
This means you can send it to different threads etc with safety.
Thread
use std::thread;
and spawn it as
let handle = thread::spawn(move || { // do stuff in here });
and wait it to finish with
handle.join().unwrap();
good for parallel stuff, not so good if sharing same cpu (context switching overhead)
for just awaiting things like IO, use async/await instead
using crossbeam::channel for testing purposes
need to drop transmitter to end child threads waiting the receiver.
Final Project - Invaders
Using many libraries fromhttps://github.com/CleanCut/rusty_engine