Rust — Ownership
The single most important concept in Rust
Every value has exactly one owner — a variable responsible for it. When the owner leaves scope, the value is dropped (freed). That's Rust's entire memory management story.
{
let s = String::from("heap data"); // s owns the allocation
println!("{}", s);
} // ← s out of scope → drop() runs → memory freed
No garbage collector. No manual free. Deterministic cleanup at brace-close.
Assignment MOVES ownership
For heap data, assignment transfers ownership instead of copying:
let s1 = String::from("hello");
let s2 = s1; // MOVE: s1 is now invalid
println!("{}", s1); // ❌ compile error: borrowed after move
println!("{}", s2); // ✓ s2 is the sole owner now
Before: s1 ──> "hello" (heap)
After: s2 ──> "hello" (heap) s1 = invalidated on purpose
Why? Both variables would otherwise think they own the same heap block — and both would free it at scope end (a double-free crash). Rust prevents that by invalidating s1 at compile time.
Functions take ownership too
Passing a value to a function moves it:
fn consume(s: String) {
println!("{}", s);
} // s dropped here
fn main() {
let text = String::from("mine");
consume(text);
println!("{}", text); // ❌ moved into consume()
}
Getting the value back means returning it:
fn consume_and_return(s: String) -> String {
println!("{}", s);
s // hand ownership back to caller
}
Copy types are the exception
Simple stack values (integers, floats, bools, chars) implement Copy — assignment duplicates instead of moving:
let a = 5;
let b = a; // copied, not moved
println!("{} {}", a, b); // 5 5 ✓ both alive
Copy types: all integers/floats/bools/chars, and tuples/arrays containing only Copy members. Anything touching the heap (String, Vec) moves.
clone() — explicit deep copy
When you want BOTH variables valid:
let s1 = String::from("hello");
let s2 = s1.clone(); // real duplicate allocation
println!("{} {}", s1, s2); // ✓ both fine — two separate strings
clone() is deliberately loud: expensive copies should be visible in code review.
The escape hatches (previews)
Ownership rules feel restrictive until you learn:
| Tool | Meaning | Lesson |
|---|---|---|
References &s | borrow without taking ownership | Borrowing |
&mut s | temporary exclusive write access | Borrowing |
clone() | pay for a real copy | this lesson |
90% of real code borrows rather than clones — borrowing is the next lesson's whole topic.
Move semantics apply to structs/vectors identically
struct Config { debug: bool }
let c1 = Config { debug: true };
let c2 = c1; // moved
// println!("{}", c1.debug); ❌ same rule as String
Mini Practice
- Trigger "borrow of moved value"; read the full compiler note including "move occurs because…".
- Fix it with
.clone(); then fix it by returning from the function. - List which of these Copy: i32, String, bool, char, (i32, f64), Vec<i32>.
- Write consume(text) -> String and chain two consumes.
Next: borrowing →
Related Topics
Frequently Asked Questions about Ownership
What is Ownership in Rust?
Ownership is a fundamental concept in Rust. This lesson explains it step by step with clear examples, making it easy for beginners to understand.
How do I learn Ownership?
Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Ownership.
Why is Ownership important in Rust?
Ownership is essential for Rust development. Understanding this concept will help you write better code and solve real-world problems more effectively.