Skip to content

Beginner · Ownership: Rust's big idea · topic 11 of 50 · 35 min

The ownership rules and moves

Explanation

Rust has three ownership rules: each value has one owner; there is only one owner at a time; when the owner goes out of scope, the value is dropped (freed). Assigning a String to another variable moves it: the old variable can no longer be used. This is how Rust avoids double frees without a garbage collector.

The three rules

  1. Each value in Rust has an owner: the variable that holds it.
  2. There can only be one owner at a time.
  3. When the owner goes out of scope, the value is dropped (its memory is freed).

Scope is the part of the code where a variable is valid: from the line where it is declared to the closing curly brace of its block. Drop means "clean up": for a String, the heap bytes are freed. Rust calls a special function named drop for you automatically at the closing brace.

rust
{                                   // s is not valid yet
    let s = String::from("chai");   // s is valid from here
    println!("{s}");                // use s
}                                   // scope ends: s is dropped, heap memory freed
No free() call, no garbage collector: the closing brace is the cleanup point.

What happens on assignment: a move

Now the key question. What does let s2 = s1; do when s1 is a String? Rust copies only the stack part (pointer, length, capacity). Both variables would then point to the same heap bytes.

output
let s1 = String::from("dosa");
let s2 = s1;

  STACK                         HEAP
  s1: ptr --+  (no longer valid)
      len 4  \
      cap 4   \              +---+---+---+---+
               +----------> | d | o | s | a |
  s2: ptr ----/              +---+---+---+---+
      len 4
      cap 4
Only the stack part is copied. Rust then treats s1 as invalid.

If both s1 and s2 were valid, then at the end of the scope both would be dropped, and the same heap memory would be freed twice: a double free. Rust prevents this simply: after let s2 = s1;, the value has moved to s2, and s1 is no longer usable. Only s2 will drop the memory.

output
let s1 = String::from("dosa");
let s2 = s1;
println!("{s1}");
// error[E0382]: borrow of moved value: `s1`
//   value moved here: let s2 = s1;
//   value borrowed here after move
The compiler stops you from using a moved value.
Think of it like this: A move is like handing over your car key. There is one key. After you give it to your friend, she owns the car and you cannot drive it any more. When she sells the car, it is gone; you cannot sell it a second time.

Moves are cheap

A move copies only 24 bytes (three numbers on a 64-bit machine), no matter whether the string has 4 letters or 4 million. The heap data is never copied by a move. So Rust is both safe and fast here.

Moves happen in many places

  • Assignment: let b = a;
  • Passing to a function: takes_it(a); (next topic but one)
  • Returning from a function: the value moves to the caller.
  • Putting into a collection: list.push(a); moves a into the list.
  • A for loop over a collection by value: for x in list moves each item out, and list is used up.

Assigning a new value drops the old one

rust
let mut s = String::from("old");
s = String::from("new");   // "old" is dropped right here
println!("{s}");           // new
The old heap memory is freed immediately when it is replaced.

Drop runs in reverse order

When several variables go out of scope at the same brace, they are dropped in the reverse order of creation: the last one created is dropped first. This matters later, when one value refers to another.

Common mistakes and fixes

  • Using a variable after moving it (E0382). Fix: use the new owner; or borrow with & instead of moving (module 4); or call .clone() if you really need two copies.
  • Moving inside a loop: for i in 0..3 { send(msg); } moves msg on the first round. Fix: send(&msg) if the function can borrow, or msg.clone() if it must own.
  • Thinking a move copies the heap data. It does not; it only transfers ownership.
Tip: When you see "value moved here" in an error, the compiler points to the exact line of the move. Ask: did that line really need ownership, or would a reference do?

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

The ownership rules and moves · 1/7

1. One owner 2. One at a time 3. Out of scope → dropped

Ownership has three rules. Each value has one owner. Only one owner at a time. When the owner goes out of scope, the value is dropped.

Prefer a video by a teacher? Find free videos on “rust ownership rules move semantics explained” on YouTube ↗

In real life: Handing an order to the kitchen

In a restaurant ordering system, an Order (with a heap-allocated list of dishes) is created at the counter and then moved into the kitchen queue. After the move, the counter code cannot accidentally change or re-send the same order: the compiler forbids touching it. Exactly one part of the program owns each order, and it is freed exactly once when the kitchen finishes it.

rust
fn main() {
    let order = String::from("2 masala dosa, 1 filter coffee");
    let mut kitchen_queue: Vec<String> = Vec::new();
    kitchen_queue.push(order);          // order moves into the queue
    // println!("{order}");             // error: borrow of moved value
    println!("{:?}", kitchen_queue);
}
After push, the queue owns the order.

Practice (0/3 done)

Task 1 · Question

Does this compile? let a = String::from("idli"); let b = a; println!("{b}");

Task 2 · Question

Which line causes the error? 1: let x = String::from("vada"); 2: let y = x; 3: let z = y; 4: println!("{y}");

Task 3 · Practice

Write a program that creates a String, moves it to a second variable, and then tries to print the first. Read the full compiler error. Then fix it in two different ways: (a) print the second variable instead, (b) use .clone() so both are valid.

Quick quiz

Question 1

1.When is a value dropped?

Question 2

2.What does let s2 = s1; do when s1 is a String?

Question 3

3.Why does Rust invalidate s1 after a move?

Question 4

4.How much data does a move of a 1 MB String copy?

Question 5

5.let mut s = String::from("a"); s = String::from("b"); What happens to "a"?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

When is a value dropped?

Answer: When its owner goes out of scope

Rule 3: the value is dropped when the owner goes out of scope.

What does let s2 = s1; do when s1 is a String?

Answer: Moves ownership to s2; s1 can no longer be used

Assignment of a non-Copy value moves it.

Why does Rust invalidate s1 after a move?

Answer: To prevent the heap data being freed twice

If both owned the data, both would drop it, causing a double free.

How much data does a move of a 1 MB String copy?

Answer: Only the small stack part (pointer, length, capacity)

A move copies the three stack numbers; the heap bytes stay where they are.

let mut s = String::from("a"); s = String::from("b"); What happens to "a"?

Answer: It is dropped when s is reassigned

Assigning a new value drops the old value immediately.

How does a function give ownership back to the caller?Related

Answer: By returning it

Returning a value moves its ownership to the caller.

From Ownership and functions

How does Rust decide when to free heap memory?Related

Answer: Ownership rules let the compiler insert the free at the right point

Rust frees a value when its owner goes out of scope, decided at compile time.

From Memory 101: the stack and the heap

How does Rust free memory?Related

Answer: Through ownership: a value is freed when its owner goes out of scope

Ownership rules make freeing automatic and predictable, and the compiler checks them.

From What Rust is and why learn it

What is the best fix for "returns a reference to data owned by the current function"?Related

Answer: Return an owned value

Move ownership out instead of borrowing a local.

From Interview questions: ownership, borrowing and lifetimes

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Ownership: Rust's big idea” before its exam: Copy and Clone, Ownership and functions.
  2. Module 3 exam: Ownership · 12 questions · 20 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Borrowing, slices and lifetimes. 50 topics left in the course before the final exam.

← Previous: Memory 101: the stack and the heap