Rust Ownership
Ownership is the idea that makes Rust Rust. It’s how the language guarantees memory safety without a garbage collector and without you manually freeing memory. It feels unusual at first, but the core idea is simple. Let’s build it up slowly.
The problem ownership solves
Section titled “The problem ownership solves”When a program runs, it stores data in memory. At some point that memory needs to be cleaned up, or your program leaks memory and eventually crashes. Languages handle this in two common ways:
- Manual (C/C++): you free memory yourself. Forget to, and you leak. Free too early, and you get crashes and security bugs.
- Garbage collector (Java, Go, Python): a background process cleans up for you. Convenient, but it costs performance and predictability.
Rust takes a third path: it decides at compile time exactly when each value should be cleaned up, using a set of rules about ownership. No manual freeing, no garbage collector.
The three rules of ownership
Section titled “The three rules of ownership”These are the whole system. Everything else follows from them:
- Each value has a single owner (a variable that “owns” it).
- There can only be one owner at a time.
- When the owner goes out of scope, the value is automatically cleaned up.
Let’s see each rule in action.
Rule 3: values are dropped at the end of scope
Section titled “Rule 3: values are dropped at the end of scope”A scope is the region between { and }. When a variable’s scope ends, Rust automatically frees its value — you never write free():
fn main() { { let message = String::from("hello"); // message is valid here println!("{message}"); } // <- scope ends, `message` is automatically cleaned up
// println!("{message}"); // ❌ error: message no longer exists here}You didn’t have to free anything — Rust did it the moment message went out of scope.
Rules 1 & 2: the “move”
Section titled “Rules 1 & 2: the “move””Here’s where it gets interesting. Watch what happens when you assign one variable to another:
fn main() { let s1 = String::from("hello"); let s2 = s1; // ownership MOVES from s1 to s2
println!("{s2}"); // ✅ works // println!("{s1}"); // ❌ error: s1 was moved}When you write let s2 = s1, Rust doesn’t copy the string — it moves ownership from s1 to s2. Because rule 2 says there can only be one owner, s1 is now invalid. Trying to use it gives:
error[E0382]: borrow of moved value: `s1`What if I want two copies?
Section titled “What if I want two copies?”If you actually want a second, independent copy, call .clone():
fn main() { let s1 = String::from("hello"); let s2 = s1.clone(); // makes a full copy
println!("s1 = {s1}, s2 = {s2}"); // ✅ both work now}clone() is explicit on purpose — copying can be expensive, so Rust makes you ask for it.
Simple types are copied, not moved
Section titled “Simple types are copied, not moved”You might wonder why this doesn’t cause an error:
fn main() { let x = 5; let y = x; println!("x = {x}, y = {y}"); // ✅ both work}Small, fixed-size values like integers, floats, booleans, and char are cheap to copy, so Rust just copies them instead of moving. As a beginner, the rule of thumb is:
- Simple values (numbers,
bool,char) → copied. The original stays valid. - Owned data (like
String) → moved. The original becomes invalid.
Ownership and functions
Section titled “Ownership and functions”Passing a value to a function moves it too — just like assigning to a variable:
fn take_ownership(s: String) { println!("I now own: {s}");} // s is dropped here
fn main() { let text = String::from("hello"); take_ownership(text); // println!("{text}"); // ❌ error: text was moved into the function}This is exactly why the next topic — borrowing — exists. Borrowing lets a function use a value without taking ownership of it, so you can keep using it afterward.
- Ownership lets Rust manage memory safely with no garbage collector.
- Three rules: one owner per value, one owner at a time, value dropped when the owner leaves scope.
- Assigning owned data (like
String) moves it — the original becomes invalid. - Use
.clone()for an independent copy; simple types (numbers,bool,char) are copied automatically. - Passing a value to a function moves it too — which leads us to borrowing.
Quick check
Section titled “Quick check”Quick check
1. How many owners can a value have at one time in Rust?
2. What method do you call to make an independent copy of a String instead of moving it?
3. Which of these types is copied automatically instead of moved when assigned to another variable?
4. What happens when you pass a String to a function that takes it by value (not by reference)?
5. When does Rust automatically clean up (drop) a value, according to this page?
Score: 0 / 5