Ownership, Borrowing & Lifetimes

Ownership rules, references, slices, and lifetimes — Rust's core differentiator.

Each value has one owner. When you assign or pass by move, the previous binding is invalid — the compiler catches use-after-move at compile time.

Core concepts

  1. Move semantics: Non-Copy types (e.g. String) transfer ownership on assignment. — The Rust Book: What is ownership
  2. Borrowing: &T immutable references; &mut T exclusive mutable borrow — no data races. — The Rust Book: References and borrowing
  3. Slices: &str for UTF-8 strings; &[i32] for array views without copying. — The Rust Book: Slices
  4. Lifetimes: Annotations when the compiler can't infer reference validity — fn longest<'a>(x: &'a str, y: &'a str) -> &'a str. — The Rust Book: Lifetimes
  5. Clone vs Copy: Explicit .clone() when you need a duplicate heap allocation. — The Rust Book: Clone
rust
fn len(s: &str) -> usize {
    s.len()
}

fn main() {
    let name = String::from("Ada");
    let n = len(&name);
    println!("{name} has {n} chars");
}

Concept videos

YouTube lessons for these topics. On a large screen they open in the reading panel next to the roadmap, same as the article links on each core concept.

Explanation

Ownership is Rust’s library checkout system: each book has one borrower responsible for returning it. When you move a value, you hand the card to someone else — you cannot still claim you have the book.

Move semantics

Heap data like String moves by default — the old variable is invalid afterward. Small integers implement Copy and duplicate silently. Moves prevent double-free bugs without a garbage collector.

Borrowing

&T lets many readers; &mut T allows one writer and no readers at the same time. The borrow checker enforces this at compile time — data races become compile errors, not Heisenbugs.

Slices

&str borrows UTF-8 text; String owns it. Functions taking &str accept both String references and literals — flexible APIs without unnecessary allocation.

Lifetimes

Lifetime parameters tell the compiler how long references stay valid when multiple inputs combine. Most code infers lifetimes; explicit 'a appears in helpers returning references from parameters.

Clone vs Copy

Copy is implicit bitwise duplicate for simple types. Clone is explicit duplication — may allocate. Clone when you truly need two owners; borrow when one owner suffices.

What Is It?

Ownership tracks who frees memory when values go out of scope.

Borrowing lets functions use data without taking ownership via references.

Lifetimes annotate relationships so references never outlive their data.

Why Should I Care?

This is Rust's headline feature — interviews and code reviews center on move/borrow decisions.

Understanding slices (&str, &[T]) unlocks idiomatic APIs.

Real World Analogy

Ownership is a library book policy: one borrower at a time for mutable lending, many readers for shared references, and fines (compile errors) if you return a book you already gave away.

Visual Mental Model

text
move: owner transfers
  &T: many readers
  &mut T: one writer

Beginner Explanation

Each value has one owner. Borrow with & and &mut instead of copying everything. Lifetimes label how long borrows stay valid.

Smallest Possible Example

rust
fn len(s: &String) -> usize {
    s.len()
}

fn main() {
    let msg = String::from("hi");
    println!("{}", len(&msg));
}

Common Mistakes

❌ Returning reference to a local variable.

Dangling reference — compiler blocks it unless lifetimes prove safety.

❌ Holding &mut while also using & to the same data.

Violates aliasing rules — split scopes or restructure.

❌ Fighting the borrow checker with Rc everywhere.

Shared mutability has tools (RefCell, Arc) but learn simple borrows first.

Real Project Example

Tokio async runtimes rely on Send/Sync ownership rules for task safety.

Systems teams at AWS use Rust ownership to eliminate use-after-free in parsers.

Build This Mini Project

Write a function that takes &str, returns length, then try moving the String into another function — observe move errors and fix with borrow.

Interview Question

Explain ownership in one minute.

Each value has a single owner responsible for cleanup. Assignment moves ownership unless Copy types. You can lend with immutable or mutable references under strict aliasing rules enforced at compile time.

Key Takeaways

  • One owner per value.
  • Borrow instead of clone when possible.
  • Lifetimes document reference validity.
  • Read compiler errors — they teach rules.

Resources

The 60-second story

A food truck has one legal owner for the cash box. If you hand the box to your coworker, you do not still have it. You had it. They have it. That handoff is a move. Trying to open the box after you handed it over is how people get robbed by their own code, so Rust will not let the program start.

Borrowing is different. You let someone look at the ledger (&T) or you let exactly one person write in it (&mut T). Lookers can crowd around. The writer gets the book alone. That rule is the whole “no data race” pitch in one gesture: nobody writes while someone else is also writing or even reading a stale page.

A slice is not a second cash box. &str is a window onto text someone else owns. &[i32] is a window onto a row of numbers. You can pass the window around without cloning the row. Lifetimes are the note that says how long the window is allowed to stay open. When the compiler can see the note itself, you stay quiet. When two windows come back and the compiler cannot tell which owner outlives the return, you write 'a so the note is explicit.

Copy types are tickets that are cheap to photocopy, like integers. String is not a ticket. It is the box. If you need a second box, you .clone() and you feel the cost because the method is sitting in the source.

Map the truck.

  • One owner is the person holding the cash box.
  • A move is handing the box over.
  • &T is a look, many allowed.
  • &mut T is the only pen.
  • A slice is a window, not a second box.
  • A lifetime is how long the window may stay open.
  • clone is paying for another box on purpose.
rust
fn shout(name: &str) {
    println!("{name}");
}

shout borrows the text. The caller still owns it after the call. If the function took String by value, the caller would be empty-handed unless they cloned first. Beginners hit that and think Rust is rude. Rust is refusing a second owner of one box.

The feature exists because manual free in C is a memory of who was supposed to clean up, and humans are bad at that memory. Garbage collection hires a cleaning crew later. Rust says the owner cleans up when the owner goes out of scope, and the borrow rules keep the cleanup from happening while someone is still looking.

Mental model: one legal owner of the cash box. Looking is free and shared. Writing is exclusive. Cloning is a conscious expense.

If you can act out “I handed you the box, so I cannot open it” with your hands, you can explain ownership without the word “affine.”

Threads in the same domain as this chapter — go deeper on iHateReading without leaving the roadmap.

Web scraping cover

Web scraping

Fetch and parse web data — the baseline skill behind browser-facing agents.

The whole fight for AI sandbox cover

The whole fight for AI sandbox

Why execution boundaries matter before you give agents shell or browser access.