Rust: Ownership & Borrowing
What you will learn
Rust's most unique feature: the ownership system that guarantees memory safety without a garbage collector.
Ownership rules (memorize these)
- Each value in Rust has exactly one owner.
- When the owner goes out of scope, the value is dropped (freed).
- You can borrow a reference without taking ownership.
Move semantics
let s1 = String::from("hello");
let s2 = s1; // ownership MOVES from s1 to s2
// println!("{}", s1); // compile error -- value moved
When s1 is assigned to s2, the String data does not get copied. Instead, the ownership moves. s1 is no longer valid -- Rust prevents use-after-move at compile time.
Clone (deep copy)
If you want a deep copy instead of a move:
let s1 = String::from("hello");
let s2 = s1.clone(); // deep copy
println!("{}, {}", s1, s2); // both valid
Borrowing with references
A reference (&) lets you use a value without taking ownership:
fn calc_len(s: &String) -> usize {
s.len()
} // s goes out of scope, but it was only borrowed -- s1 keeps ownership
let s1 = String::from("hello");
let len = calc_len(&s1);
println!("{}", s1); // still valid because we only borrowed
Mutable references
To modify a borrowed value, use &mut:
fn change(s: &mut String) {
s.push_str(" world");
}
let mut s = String::from("hello");
change(&mut s);
println!("{}", s); // "hello world"
Important rule: you can have either one mutable reference or any number of immutable references, but never both at the same time.
Common mistakes
- Trying to use a variable after a move -- Rust is strict about this. Clone if you need both.
- Creating two mutable references to the same data -- Rust rejects this at compile time to prevent data races.
- Thinking
=always copies -- it only copies for simple types like integers (which implementCopy). Strings and most other types move.
Quick check below!