Ownership

The Ownership Rules

Rust's entire memory-safety guarantee rests on three foundational ownership rules enforced at compile time by rustc:

  1. Every value in Rust has an owner (a variable binding, struct field, or collection element that holds it).
  2. There can only be one owner at a time (transferring ownership to a new binding immediately invalidates the old binding).
  3. When the owner goes out of scope, the value is dropped (its destructor runs and its memory is freed automatically).

Visualizing the Three Rules

Rule 1: Every value has an owner
  let s1 = String::from("data");   --> `s1` owns the heap buffer

Rule 2: Only one owner at a time
  let s2 = s1;                     --> `s2` is now the sole owner; `s1` is invalidated!

Rule 3: When the owner goes out of scope, the value is dropped
  }                                --> `s2` goes out of scope at `}`; heap buffer is freed once!

Rule 1: Every Value Has an Owner

Whenever a value is created in Rust, it is either bound to a variable (let x = ...;), stored inside an owning container (such as a Vec<T>, tuple, or struct field), or held in an unnamed temporary slot for the duration of the enclosing expression or statement:

struct DropLogger(&'static str);

impl Drop for DropLogger {
    fn drop(&mut self) {
        println!("  [Drop] {}", self.0);
    }
}

fn main() {
    println!("1. Creating a named owner:");
    let _named = DropLogger("named variable (lives until end of main)");

    println!("2. Creating an unnamed temporary value:");
    DropLogger("unnamed temporary (dropped at the `;` on this line!)");

    println!("3. Still inside main before closing brace...");
}
1. Creating a named owner:
2. Creating an unnamed temporary value:
  [Drop] unnamed temporary (dropped at the `;` on this line!)
3. Still inside main before closing brace...
  [Drop] named variable (lives until end of main)

Notice that even a value that is not assigned to a let variable has a well-defined owner (a temporary stack slot) that drops the value at the semicolon terminating that statement.

Rule 2: There Can Only Be One Owner at a Time

For any type that manages heap memory or system resources (does not implement Copy), assigning the variable to another variable, passing it by value to a function, or putting it into a struct or collection transfers ownership (performs a move) to the new owner:

fn take_ownership(received: String) {
    println!("Inside take_ownership: {received}");
} // `received` is the sole owner here, so the String is dropped when `take_ownership` exits!

fn main() {
    let first_owner = String::from("exclusive resource");

    // 1. Move ownership from `first_owner` to `second_owner`:
    let second_owner = first_owner;
    // `first_owner` is no longer valid here!

    // 2. Move ownership from `second_owner` into parameter `received`:
    take_ownership(second_owner);
    // `second_owner` is no longer valid here!
}
Inside take_ownership: exclusive resource

Attempting to read first_owner after moving it to second_owner violates Rule 2 and is rejected at compile time (E0382):

// ❌ does not compile
fn main() {
    let first_owner = String::from("exclusive resource");
    let second_owner = first_owner;

    println!("{first_owner} and {second_owner}");
}
error[E0382]: borrow of moved value: `first_owner`
 --> src/main.rs:5:15
  |
2 |     let first_owner = String::from("exclusive resource");
  |         ----------- move occurs because `first_owner` has type `String`, which does not implement the `Copy` trait
3 |     let second_owner = first_owner;
  |                        ----------- value moved here
4 |
5 |     println!("{first_owner} and {second_owner}");
  |               ^^^^^^^^^^^^^ value borrowed here after move

error: aborting due to 1 previous error

Rule 3: When the Owner Goes Out of Scope, the Value Is Dropped

A variable's scope begins where it is declared and ends at the closing brace } of its enclosing block. If the variable still owns its value when execution leaves that block (via normal fallthrough, return, break, continue, or panic unwinding), the compiler automatically calls Drop::drop on the value:

  • Variables in the same block drop in reverse declaration order (Last-In, First-Out).
  • Struct fields and tuple elements drop in declaration order (first field drops first) when their parent struct or tuple is dropped.
  • Variables whose ownership was already moved elsewhere are not dropped when their block ends, because the new owner is now responsible for dropping the value.
struct Item(&'static str);

impl Drop for Item {
    fn drop(&mut self) {
        println!("  [Drop] {}", self.0);
    }
}

fn main() {
    let kept_in_main = Item("1. kept_in_main");
    let moved_into_inner = Item("2. moved_into_inner");

    {
        let inner_only = Item("3. inner_only");
        let _took_ownership = moved_into_inner; // Ownership moves into the inner block!
        let _ = inner_only;
        println!("--- Exiting inner block ---");
    } // Both `_took_ownership` ("2. moved_into_inner") and `inner_only` drop here!

    let _ = kept_in_main;
    println!("--- Exiting main ---");
} // Only `kept_in_main` drops here!
--- Exiting inner block ---
  [Drop] 2. moved_into_inner
  [Drop] 3. inner_only
--- Exiting main ---
  [Drop] 1. kept_in_main

Summary of How the Three Rules Interact

Operation Effect on Old Owner Effect on New Target Who Drops the Value?
let b = a; (Non-Copy type) a becomes uninitialized (moved) b becomes sole owner b when b goes out of scope
let b = a; (Copy type, e.g., i32) a owns original bitwise value b owns independent bitwise copy Both are trivial stack values (no heap drop)
let b = a.clone(); a retains ownership of original b owns newly duplicated buffer Both a and b drop their own buffers independently
let r = &a; (Borrowing) a retains ownership r borrows read-only access a when a goes out of scope