Lesson 6
Ownership
Understand Rust's most unique feature: ownership, move semantics, and the Copy trait.
The Big Idea
Ownership is Rust's secret weapon -- it's how Rust guarantees memory safety without a garbage collector. There are three rules:
- Each value has one owner (a variable).
- There can only be one owner at a time.
- When the owner goes out of scope, the value is dropped (freed).
Move Semantics
When you assign a heap-allocated value (like a String) to another variable, ownership moves. The original variable becomes invalid -- you can't use it anymore. This prevents double-free bugs.
let s1 = String::from("hello");
let s2 = s1; // s1 is MOVED to s2
// println!("{}", s1); // ERROR: s1 is no longer valid
Copy Types
Simple types like integers, floats, and booleans implement the Copy trait. They're copied instead of moved, so both variables remain valid. Stack-only data is cheap to copy.
Run the code below. Then try uncommenting the marked line to see what the compiler says.
Try it
fn main() {
// --- Integers are COPIED ---
let a = 42;
let b = a; // a is copied, both are valid
println!("a = {}, b = {}", a, b);
// --- Strings are MOVED ---
let s1 = String::from("hello");
let s2 = s1; // s1 is moved to s2
println!("s2 = {}", s2);
// Uncomment the next line to see a compile error:
// println!("s1 = {}", s1); // ERROR: value borrowed after move
// --- Clone: explicit deep copy ---
let s3 = String::from("world");
let s4 = s3.clone(); // deep copy, both are valid
println!("s3 = {}, s4 = {}", s3, s4);
// --- Ownership and functions ---
let name = String::from("Rust");
take_ownership(name);
// name is no longer valid here -- it was moved into the function
let x = 10;
make_copy(x);
println!("x is still valid: {}", x); // works because i32 is Copy
// --- Getting ownership back ---
let greeting = String::from("hi there");
let greeting = give_and_take(greeting);
println!("Got it back: {}", greeting);
}
fn take_ownership(s: String) {
println!("I own: {}", s);
} // s is dropped here
fn make_copy(n: i32) {
println!("I got a copy: {}", n);
}
fn give_and_take(s: String) -> String {
println!("Passing through: {}", s);
s // return ownership back to caller
}Challenge
Try uncommenting the line that causes a compile error. Read the error message carefully -- Rust's compiler gives excellent explanations.
Previous: Control Flow
Next: References & Borrowing