← Back to Rust series
🦀
Ownership
Ownership · Prerequisite: lessons 1-5

06. The Three Rules of Ownership + move/copy

Ownership is the mechanism by which Rust guarantees memory safety without a garbage collector. Three rules suffice — each value has exactly one owner, the value is dropped when its owner goes out of scope, and assignment / argument passing moves ownership. This lesson covers move vs. copy, the meaning of Drop, and leads naturally into borrowing.

RustownershipmovecopyDropRAII
Duration
~1.5 hours
Level
📊 Intermediate
Prerequisite
🎯 Lessons 1-5
OUTCOME
Ownership is the mechanism by which Rust guarantees memory safety without a garbage collector. Three rules suffice — each value has exactly one owner, the value is dropped when its owner goes out of scope, and assignment / argument passing moves ownership. This lesson covers move vs. copy, the meaning of Drop, and leads naturally into borrowing.

What you'll learn

  • 1State the three ownership rules in your own words
  • 2Spot where a move happens in real code
  • 3Distinguish Copy types from non-Copy types
  • 4Know when Drop runs
  • 5Read use-after-move compiler errors and locate the root cause

Overview

Most languages solve memory management one of two ways — **manual (C/C++ malloc/free)** or **automatic GC (Java/Python)**. Manual is fast but dangerous; GC is safe but adds STW pauses. Rust takes a third path — **the compiler enforces ownership rules**, giving you safety without a GC at runtime.

Core Concepts

1) The three rules

  1. Each value has **exactly one owner**
  2. Only one owner exists at any given time
  3. When the owner **goes out of scope**, the value is dropped automatically

2) move — transferring ownership

Assigning or passing a value that owns heap data (String, Vec, ...) **transfers ownership**. The original binding becomes invalid.

rust
let s1 = String::from("hello");
let s2 = s1;          // s1's ownership moves to s2
println!("{}", s1);   // error[E0382]: borrow of moved value: `s1`

3) Copy — types where bitwise copy is safe

Types that **live only on the stack** (i32, f64, bool, char, fixed-size arrays of Copy types) implement the Copy trait — assignment produces a duplicate, not a move.

rust
let x = 5;
let y = x;            // copy, not move
println!("{} {}", x, y); // both alive

4) Drop — destruction hook

When the owner goes out of scope, `Drop::drop` runs automatically, releasing heap memory, file handles, sockets, etc. This pattern is **RAII** (Resource Acquisition Is Initialization).

TypeAssignmentWhy
i32, bool, charCopyStack only, bitwise safe
String, Vec<T>MoveHeap pointer — two owners → double-free
&T (reference)CopyBorrowing, no ownership transfer
(i32, i32)CopyAll components are Copy
(i32, String)MoveString pulls the whole tuple into Move

Hands-on Examples

Passing an argument moves ownership:

rust
fn takes_ownership(s: String) {
    println!("{}", s);
} // s is dropped here

fn main() {
    let s = String::from("hello");
    takes_ownership(s);
    // println!("{}", s); // error — s has been moved
}

Hand it back to keep using it (borrowing is nicer — this is for contrast):

rust
fn take_and_give(s: String) -> String {
    println!("len={}", s.len());
    s
}

fn main() {
    let s1 = String::from("hello");
    let s2 = take_and_give(s1);
    println!("{}", s2); // OK — s2 is the new owner
}

Observe Drop firing:

rust
struct Resource { name: String }
impl Drop for Resource {
    fn drop(&mut self) { println!("drop: {}", self.name); }
}
fn main() {
    let _r = Resource { name: "file.txt".into() };
    println!("end of main");
} // prints "end of main" then "drop: file.txt"

Common Mistakes

Q. After passing a String to a function I want to use it again — do I always return it?

A. That's exactly what **borrowing (`&`)** solves in the next lesson — lend without transferring ownership. For now, get the move mechanic locked in.

Q. `let s2 = s1.clone()` leaves both alive — what's that?

A. `.clone()` is an **explicit deep copy** — allocates a new heap buffer. You pay memory / time cost for two independent owners.

Q. How do I make my own struct Copy?

A. Add `#[derive(Copy, Clone)]`. Only valid if **every field is Copy**. A struct containing a String can't be Copy.

Recap

  • Each value has one owner; end of scope = drop
  • Assigning or passing heap-owning values = move, original invalidated
  • Stack-only types are Copy, so assignment duplicates
  • Drop automates RAII-style resource release

Try It Yourself

  1. Move a String into a function, then try to use it after — note the compiler error
  2. Create two Resource structs and observe drop order at end of main (LIFO?)
  3. Derive Copy on a struct containing only i32, then try the same with a struct containing String
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗