Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Ownership in One Page

If you only remember one thing: every value in Rust has exactly one owner, and when the owner goes out of scope the value is freed — automatically, at a time you can predict.

The rule

Rust has exactly three ownership rules. You will read them in twenty seconds and spend the rest of the week getting used to them.

The three rules
  1. Every value has exactly one owner.
  2. When the owner goes out of scope, the value is dropped (memory freed, file closed, lock released).
  3. Ownership can be moved from one name to another, but it cannot be in two places at once.

The library card analogy

The mental model

One library card. One holder. At all times.

1. OWN Ada book let ada = String::from("book"); Ada holds it. 2. MOVE Ada Ben let ben = ada; Ada no longer has it. 3. DROP Ben } // end of scope value is dropped.
Ownership is a library card. Exactly one person holds it. When they leave, the book goes back to the shelf.

The code that shows move

fn main() {
    let ada = String::from("book");
    let ben = ada;          // ownership moves to ben
    println!("{ben}");      // fine
    // println!("{ada}");   // would not compile
}

▶ Run this in the Rust Playground

If you uncomment the last line, Rust says:

error[E0382]: borrow of moved value: `ada`

That is the whole behavior. Read it as “Ada gave the card to Ben. She does not have it anymore.”

Plain English

If this feels restrictive — good, you're reading it right. It is restrictive. In exchange, you get a language where "use after free", "double free", and "data race" are compile errors, not 3 a.m. pages. That is the deal.

Why some values “copy” instead of “move”

Try this and it works fine:

fn main() {
    let a = 5;
    let b = a;
    println!("{a} {b}");  // both are valid
}

▶ Run this in the Rust Playground

Integers, booleans, floats, and a few other small types are Copy. Copying them is cheap and has no cleanup cost. Rust doesn’t move them — it duplicates them bit-for-bit and everyone is happy.

String, Vec<T>, HashMap, file handles, locks — none of these are Copy. They own a resource (heap memory, a file descriptor, a lock) that has cleanup cost. Rust refuses to silently duplicate them.

Rule of thumb

If it's a plain number or a small fixed-size value, it copies. If it owns memory on the heap, a file, or a connection, it moves. When in doubt, assume move.

What “scope” means

A value’s scope is the region of code where its owner is visible. Usually that is a block — the { ... } it was declared in. When that block ends, Rust calls drop on the value.

fn main() {
    {
        let msg = String::from("hello");
        println!("{msg}");
    } // msg is dropped here. its memory is freed, right now, for sure.

    // println!("{msg}"); // would not compile; msg is out of scope
}

drop is deterministic. Not garbage-collected. Not “sometime later”. The moment the block ends — that is when the cleanup happens.

Plain English

Rust does not have a garbage collector. It does not need one. The compiler already knows exactly when to free every value — at the end of the scope that owns it.

Now watch the library card change wallets. This is the whole chapter in one animation.

Interactive simulation (requires JavaScript): assigning ada to ben moves ownership of the heap buffer without copying it; using ada afterwards fails with E0382 because two owners would free the same memory twice.

## Try this
Five-minute exercises
  1. Write let s1 = String::from("hi"); let s2 = s1; and try to print s1. Read the error.
  2. Change let s1 = String::from("hi"); to let s1 = 42; and rerun. Why does it work now?
  3. Put a String inside a block { } and try to use it after the block ends. Read the error.

Ownership says “only one owner at a time”. But often we need to look at a value without taking it over. That is borrowing — the next chapter.

Borrowing in one page →