Rust
- No garbage collector (fast runtime)
- Memory safety
- Course: https://www.youtube.com/watch?v=BpPEoZW5IiY
Crate
- It's a package, a project, a library
main.rs
andlib.rs
are the crates' root- Each crate can have several modules (a file or a directory)
Modules
- Each file in Rust (except main.rs and lib.rs) corresponds to one
module
- A module can be a
file
or adirectory
containing amod.rs
- Modules are a directory allows submodules
Expressions vs. Statements
Expression
- Evaluate to a value
- Does not change state
- Semicolon cannot be placed (otherwise the return is an empty unit)
Statement
- Perform actions, but do not return value
- Does change state! E.g., assign variables
- Semicolon is mandatory
Scopes
- Range within an item (e.g., variable) is valid
Global Scope
: accessible throughout the entire programLocal Scope
: accessible only within a function or block
Memory Allocation
Stack Memory
- LIFO
- Must have known and fixed size
- It's faster than allocating to heap, because the new data is always on the top (constant time access)
fn main() {
let x = 42;
let y = 10;
let z = add_numbers(x, y);
}
fn add_numbers(a: i32, b: i32) -> i32 {
let c = a + b;
c
}
// Stack level 0 (main): x, y, z
// Stack level 1 (add_numbers): a, b, c
-
Heap Memory
-
Allocate types with unknown sizes (e.g., arrays, strings)
- Allocating and accessing is slower (using pointers)
- The pointer to the value, however, is still pushed to the stack (pointer is fixed size)
Ownership
Owner
of a value- It's the variable or data structure that holds it
- It's responsible for allocating and freeing the memory used to store that data
- Rules
- Each value has an
owner
- There can be
only one owner
at a time - When the owner goes out of scope, the value is dropped
{
let s = "hello"; // s is alloc'ed
}
// s is freed
- Ownership drop
- Values within
stack memory
are always copied (the value itself or the pointer) - When we are talking about dynamic size values (e.g., arrays), only the pointer is copied, however the actual value in the heap is the same. When that happens,
- When a pointer is copied, the first pointer
ownership is dropped
, because there can't be 2 owners at the same time
// Copy
let x = 5;
let y = x; // x and y are independent, no ownership is dropped
// Move
let s1 = String::from("hello");
let s2 = s1; // s1 ownership is dropped (s1 is dropped)
let s3 = s2.clone(); // force copy (both ownerships are kept)
- Ownership guarantees
- Prevents
dangling pointers
(reference to nothing or trash) - Prevents
double-free
(trying to free a memory that has already been freed) - Prevents
memory leaks
(not freeing memory that should have been freed)
Borrowing
- Temporarily access data without taking ownership
- Take a
reference
(pointer), not the data itself - Rules
- At a given time: either have
one mutable ref
ormultiple immutable refs
- References must always be valid
fn main() {
let s1 = String::from("hey");
let len = calculate_length(&s1); // borrows ownership
let mut s2 = String::from("hello");
add_more(&mut s2); // borrows ownership
}
fn calculate_length(s: &String) -> usize {
s.len() // calculate len based on the pointer
}
fn add_more(s: &mut String) {
s.push_str("world");
}
// This won't compile as it will cause a dangling pointer
// Breaks the rule: reference must always be valid
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
// as dangle finishes execution s is destroy but its pointer is still returned
}