Ownership & Borrowing
This is the idea that makes Rust different from every other language on this site. Every value has exactly one owner, and Rust enforces that at compile time — which is how it guarantees memory safety without a garbage collector running in the background.
The three ownership rules
Rust's whole memory model comes down to three rules the compiler checks for every value: each value has exactly one owner at a time; when the owner goes out of scope, the value is dropped (its memory freed) automatically; and ownership can be transferred (moved) to someone else, at which point the old owner can no longer use it.
Moves: assignment can invalidate the original
For simple types like i32, assigning one variable to another copies the value — both remain usable. For a String (heap-allocated, growable text), the same assignment instead moves ownership:
fn main() { let original = String::from("hello"); let copy = original; println!("{}", original); }
error[E0382]: borrow of moved value: `original`
--> src/main.rs:5:20
|
2 | let original = String::from("hello");
| -------- move occurs because `original` has type `String`
3 | let copy = original;
| -------- value moved here
4 |
5 | println!("{}", original);
| ^^^^^^^^ value borrowed here after moveThis is not a runtime crash — it never compiles at all. let copy = original; transfers ownership of the string data to copy; original is now considered invalid, and the compiler refuses to let you use it again. This prevents a real class of C/C++ bugs: two variables both believing they own the same heap memory, and both trying to free it (a double-free) when they go out of scope.
If you genuinely want two independent copies, call .clone(), which duplicates the underlying data explicitly:
fn main() { let original = String::from("hello"); let copy = original.clone(); println!("{} {}", original, copy); }
hello hello
Borrowing: using a value without taking it
Passing a value into a function moves it by the same rule — which would make even printing a string inconvenient if that were the only option. A reference (&) lets code borrow a value temporarily without taking ownership:
fn print_length(s: &String) { println!("Length: {}", s.len()); } fn main() { let text = String::from("hello"); print_length(&text); println!("Still usable: {}", text); }
Length: 5 Still usable: hello
&text passes a reference rather than the value itself, so main still owns text after the call — nothing was moved. This is the pattern you'll reach for constantly: pass by reference when a function only needs to look at a value, and only move or explicitly clone when it genuinely needs to take ownership.
The borrowing rule: many readers, or one writer, never both
fn main() { let mut balance = 100; let r1 = &balance; let r2 = &mut balance; println!("{} {}", r1, r2); }
error[E0502]: cannot borrow `balance` as mutable because it is
also borrowed as immutable
|
3 | let r1 = &balance;
| -------- immutable borrow occurs here
4 | let r2 = &mut balance;
| ^^^^^^^^^^^^ mutable borrow occurs here
5 |
6 | println!("{} {}", r1, r2);
| -- immutable borrow later used hereRust allows either any number of immutable references (&T) at once, or exactly one mutable reference (&mut T) — never both at the same time. This is the actual rule the borrow checker enforces, and it's what rules out an entire category of bugs where one piece of code reads a value while another is midway through changing it.