# Rust From Zero

> Learn Rust from nothing to genuinely advanced: install it and the basics, then ownership - and then the deep half: lifetimes, traits and generics, smart pointers, error handling, fearless concurrency, iterators, macros, and performance and unsafe. Mental-model-first, with clear explanations.


---

# Rust From Zero

Rust has a reputation that sounds like a contradiction: it's as fast as C, but it won't let you corrupt
memory or crash on a stray pointer - and it proves that *at compile time*, before your program ever runs.
For years languages made you pick: fast and dangerous, or safe and slow. Rust's whole reason for existing
is to refuse that trade.

There's a second part to Rust's reputation, and it's also true: the learning curve is steep. The compiler
will reject programs that *look* perfectly fine, with errors about "borrows" and "moves" and "lifetimes"
that mean nothing to you yet. That's not you being bad at this. It's a genuinely new idea - **ownership** -
that no mainstream language before Rust made you think about directly. Everyone hits this wall. The good
news: it's one idea, it's learnable, and once it clicks, the compiler stops feeling like an enemy and
starts feeling like a coworker who catches your bugs before they ship.

This guide takes you the whole way: from "I've never written a line of Rust" to understanding what the
language is *actually doing* - ownership, yes, but also lifetimes, traits, smart pointers, concurrency,
and the parts of Rust that make it fast. We go mental-model-first the whole way: before any command,
you'll understand what the thing actually *is* and why Rust made the choice it did.

It's one zero-to-hero journey in two halves. **Phases 1–9 are the basics** - enough to read real Rust,
structure a project, and reason about ownership instead of guessing. **Phases 10–17 are the deep half** -
lifetimes, traits and generics, smart pointers, error handling, fearless concurrency, iterators, macros,
and performance and unsafe, the stuff that separates "writes Rust" from "understands Rust." Each phase
carries a difficulty badge so you can see the climb.

If you've never programmed at all, start with a gentler on-ramp first -
[Programming From Zero](/guides/programming-from-zero) - then come back here. Rust is a hard *first*
language; it's a great *second* one.

## How to read this

- **Brand new to Rust? Read 1–9 in order.** Each phase builds on the last. Phases 1–5 give you the
  language and how to organize it. Then phase 6 - ownership - is the whole game. Come back for 10+ when
  the basics feel comfortable.
- **Already know another language?** Skim phases 1–5 to catch where Rust is *deliberately different*
  (immutable by default, no `null`, exhaustive `match`, expressions everywhere), then **really slow down
  at phase 6.** Ownership is where Rust stops looking like a normal language and starts being Rust.
- **Past the basics already?** Jump to the deep half - [Phase 10: Lifetimes & the Borrow
  Checker](10-lifetimes-and-borrowing.md) onward is where ownership grows up into lifetimes, traits, and
  the zero-cost abstractions that make Rust fast.

## The phases

**Part 1 - The basics (🟢 Basic → 🟡 Intermediate)**
1. **[Install & Your First Program](01-install-and-first-program.md)** 🟢 - `rustup`, and `cargo run` (not raw `rustc`).
2. **[Syntax, Values & Types](02-syntax-values-and-types.md)** 🟢 - immutable by default, static types with inference, shadowing.
3. **[Collections](03-collections.md)** 🟢 - `Vec<T>`, arrays, the `String` vs `&str` confusion, `HashMap`.
4. **[Control Flow & Functions](04-control-flow-and-functions.md)** 🟢 - `if`/`match` as expressions, the loops, exhaustive matching.
5. **[Modules & Project Layout](05-modules-and-project-layout.md)** 🟢 - `Cargo.toml`, `main.rs`/`lib.rs`, `mod`/`pub`/`use`, crates.
6. **[Ownership & Borrowing](06-ownership-and-borrowing.md)** 🟡 - **the whole point of Rust:** owning, moving, borrowing, and the borrow checker.
7. **[Errors & I/O](07-errors-and-io.md)** 🟡 - `Result`/`Option` instead of exceptions and `null`, the `?` operator, files.
8. **[Ecosystem & Tooling](08-ecosystem-and-tooling.md)** 🟡 - `cargo test`, `cargo fmt`, `clippy`, crates, the toolchain you get free.
9. **[Idioms & Gotchas](09-idioms-and-gotchas.md)** 🟡 - how Rust programmers actually write Rust, and the traps that bite everyone once.

**Part 2 - Beyond the basics (🔴 Advanced)**
10. **[Lifetimes & the Borrow Checker, Deep](10-lifetimes-and-borrowing.md)** 🔴 - what lifetime annotations mean, elision, and references in structs.
11. **[Traits & Generics, Deep](11-traits-and-generics.md)** 🔴 - trait bounds, associated types, `impl` vs `dyn`, static vs dynamic dispatch.
12. **[Smart Pointers & Interior Mutability](12-smart-pointers.md)** 🔴 - `Box`, `Rc`/`Arc`, `RefCell`/`Cell`, `Deref` and `Drop`.
13. **[Error Handling, Deep](13-error-handling-deep.md)** 🟡 - `?` in depth, custom error types, `thiserror`/`anyhow`, `Option` combinators.
14. **[Fearless Concurrency](14-fearless-concurrency.md)** 🔴 - threads, `Send`/`Sync`, `Arc<Mutex<T>>`, channels, and a taste of `async`.
15. **[Closures, Iterators & Zero-Cost Abstractions](15-closures-and-iterators.md)** 🟡 - `Fn`/`FnMut`/`FnOnce`, iterator adapters, free speed.
16. **[Macros & Metaprogramming](16-macros.md)** 🟡 - `macro_rules!`, `derive`, and what procedural macros are for.
17. **[Performance, Unsafe & the Ecosystem](17-performance-and-unsafe.md)** 🔴 - zero-cost abstractions, when `unsafe` is justified, profiling, key crates.

**Finale**
18. **[Where to Go Next](18-where-to-go-next.md)** 🟢 - CLIs, web, systems, WebAssembly, and what to actually build.

> Frameworks and big domains (`async` runtimes, embedded, WebAssembly toolchains) are their own world -
> this guide makes the *language* make sense, top to bottom.


---

# Install & Your First Program

Let's get Rust onto your machine and run something real. By the end you'll have the toolchain installed
and a working program you ran yourself. We'll also clear up a question that trips up newcomers right away:
there seem to be *two* commands, `rustc` and `cargo`, and it's not obvious which to use. Short version:
`cargo` almost always - this phase shows you why.

## The mental model: a toolchain, managed by `rustup`

**What it actually is.** "Installing Rust" doesn't mean installing one program - it means installing a
*toolchain*, a bundle of tools that work together. The two you'll touch directly are:

- **`rustc`** - the actual *compiler*. Turns your `.rs` source code into a runnable program.
- **`cargo`** - Rust's *build tool and package manager*. Creates projects, downloads libraries, runs the
  compiler for you, runs your tests, and more. Think of `cargo` as the front desk and `rustc` as the
  machinery in back.

**Why there's a separate installer.** Rust releases a new stable version every six weeks, and different
projects often need different versions. So Rust installs through a small manager, **`rustup`**, whose
whole job is installing and updating the toolchain. Install it once; `rustup update` keeps things current.

📝 **Terminology.** A **toolchain** is the set of tools for one Rust version (compiler, standard library,
`cargo`, and friends). **`rustup`** installs and switches between toolchains. **`cargo`** drives your
day-to-day work. **`rustc`** is the compiler underneath.

## Install with `rustup`

Go to [rustup.rs](https://rustup.rs) and follow the instructions there - the official installer gives you
the exact command for your OS.

- **macOS / Linux:** a one-line command to paste into your terminal. It downloads `rustup`, which then
  installs the stable toolchain.
- **Windows:** a small installer (`rustup-init.exe`) to download and run. Windows also needs Microsoft's
  C++ build tools to link programs; the installer tells you if they're missing and points you to them.

Accept the default option when it asks ("1) Proceed with standard installation"), then close and reopen
your terminal so it picks up the newly installed tools.

⚠️ **Gotcha.** If your terminal says `rustc: command not found` right after installing, just **open a
fresh terminal window** - the installer adds Rust to your `PATH`, but already-open terminals don't know
about it yet. (`PATH` is the list of places your shell looks for commands; see
[Programming From Zero](/guides/programming-from-zero) if that's a new idea.)

## Confirm it worked

Two quick commands tell you the toolchain is healthy:

```console
$ rustc --version
rustc 1.95.0 (59807616e 2026-04-14)

$ cargo --version
cargo 1.95.0 (f2d3ce0bd 2026-03-21)
```
*What just happened:* Each tool printed its version, meaning it's installed and on your `PATH`. Your
numbers will be higher - Rust moves fast - but what matters is both commands answer instead of erroring.

## Create your first project with `cargo new`

Now the fun part: instead of writing a file by hand, let `cargo` scaffold a complete little project.

```console
$ cargo new hello
    Creating binary (application) `hello` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
```
*What just happened:* `cargo new hello` created a folder called `hello` containing everything a Rust
program needs to build and run - source code, a config file, and a fresh Git repository. "Binary
(application)" means a runnable program (versus a library, which other programs use). `cargo` set up the
layout correctly without you needing to know it up front.

Step inside and look at what it made:

```console
$ cd hello
$ ls
Cargo.toml  src
```
*What just happened:* `Cargo.toml` is the project's **manifest** - a small config file naming your project
and listing the libraries it depends on. `src` is the folder where your code lives. Open `src/main.rs` and
you'll find a complete program already written for you:

```rust
fn main() {
    println!("Hello, world!");
}
```
*What just happened:* This is the smallest real Rust program. `fn main()` defines the special starting
point where execution begins. `println!` prints a line of text. (The `!` means it's a *macro*, not a
normal function - read it as "print this line" for now.) Functions get unpacked properly in
[Phase 4](04-control-flow-and-functions.md).

## Run it with `cargo run`

```console
$ cargo run
   Compiling hello v0.1.0 (/home/ada/hello)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s
     Running `target/debug/hello`
Hello, world!
```
*What just happened:* One command did three jobs: `cargo run` **compiled** your code (`Compiling` and
`Finished`), **ran** the resulting program (`Running`), and printed `Hello, world!`. The compiled program
landed in a `target/` folder that `cargo` manages - never edit anything there.

💡 **Key point.** `cargo run` is the command you'll type constantly: build, then run, always. There's also
`cargo build` (build but don't run) and `cargo check` (just check it compiles, without producing a
program - the fastest way to see if your code is valid).

## Cargo is the workflow - not raw `rustc`

You *could* compile that file directly with `rustc src/main.rs` and run the program it produces - fine for
one file with no dependencies. But once your project has more than one file, or needs a library from the
internet, raw `rustc` becomes a chore of managing build commands and downloads by hand.

`cargo` exists so you don't: it knows your project layout, fetches and version-locks dependencies, compiles
everything in order, runs your tests, and more - all from short commands. **You talk to `cargo`, and
`cargo` talks to `rustc`.** Every later phase uses `cargo`.

⚠️ **Gotcha - the first compile feels slow.** The first `cargo run` (and the first run after adding a
dependency) takes noticeably longer than you'd expect, because Rust compiles a lot up front for its safety
checks and fast code. That's normal, not a hang - `cargo` caches the work afterward, so re-runs are quick
and `cargo check` is near-instant. It's the price of the speed and safety you get at runtime.

## Recap

1. **`rustup`** installs and updates the Rust toolchain; you install it once from
   [rustup.rs](https://rustup.rs).
2. **`rustc`** is the compiler; **`cargo`** is the build tool and package manager you actually drive.
3. **`cargo new <name>`** scaffolds a complete project (`Cargo.toml` + `src/main.rs`).
4. **`cargo run`** compiles and runs in one step; `cargo check` just checks it compiles (fastest).
5. **Use `cargo`, not raw `rustc`** - it scales to real projects with dependencies and tests.
6. The **first compile is slow**; that's expected, and re-builds are fast.

You have a working toolchain and a program you ran yourself. Next: fill `main` with real content - values,
their types, and Rust's default that things can't change unless you say so.


---

# Syntax, Values & Types

Now we give `main` something to work with: named values. Two things will surprise you if you're coming
from almost any other language: values **can't change by default**, and the compiler **knows the exact
type of everything** even when you don't write the types down. Neither is there to annoy you - both exist
to catch bugs early.

## `let` binds a value - and it's immutable by default

**What it actually is.** `let` creates a named value (a *variable*). The surprise: in Rust, a value bound
with `let` **cannot be changed afterward** - it's read-only unless you opt out.

```rust
fn main() {
    let x = 5;
    println!("{x}");
}
```
*What just happened:* `let x = 5;` bound the name `x` to the value `5`, and `println!("{x}")` printed it.
`{x}` inside the string is a placeholder filled with the value of `x` - Rust prints `5`.

Now watch what happens if you try to change `x`:

```rust
fn main() {
    let x = 5;
    x = 6;
    println!("{x}");
}
```
```console
$ cargo run
error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:3:5
  |
2 |     let x = 5;
  |         - first assignment to `x`
3 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable
  |
help: consider making this binding mutable
  |
2 |     let mut x = 5;
  |         +++
```
*What just happened:* The compiler **refused to build the program**: it saw you bind `x` to `5`, then try
to overwrite it with `6`, and stopped you - `let` values are immutable. Notice how good the error is: it
points at both lines, explains the problem plainly, and even shows the exact fix (`let mut x`). Rust's
errors are some of the best in any language; read them, don't fear them.

## `mut` - opt in to changing a value

If you want a value to change, say so with `mut` ("mutable"):

```rust
fn main() {
    let mut x = 5;
    x = 6;
    println!("{x}");
}
```
```console
$ cargo run
6
```
*What just happened:* Adding `mut` told Rust "this one is allowed to change," so reassigning `x` to `6`
works, and it printed `6`.

💡 **Key point.** Immutable-by-default flips the habit from other languages, where everything can change
and you mark the rare constant. In Rust, *nothing* changes unless you write `mut`. The payoff: reading
`let total = ...` with no `mut` tells you for certain `total` is never modified later, no need to scan the
rest of the function. That removes a whole category of "wait, where did this get changed?" bugs.

⚠️ **Gotcha for newcomers.** The first dozen times, you'll write `let count = 0;`, then try `count += 1;`
in a loop, and the compiler will stop you. That's not a logic bug - you just forgot `mut`. The fix is
always `let mut count = 0;`. After a week this becomes automatic.

## Static types, with inference

**What it actually is.** Rust is **statically typed**: every value has a fixed type known at compile time
(a whole number, a decimal, text, a true/false, …), and you can't accidentally mix them. But Rust also has
**type inference**: the compiler figures out the type from how you use the value, so you rarely need to
spell it out - why the examples above had no types written on them.

```rust
fn main() {
    let count = 10;       // Rust infers: i32 (a 32-bit integer)
    let price = 4.99;     // Rust infers: f64 (a 64-bit decimal)
    let active = true;    // Rust infers: bool
    println!("{count} {price} {active}");
}
```
```console
$ cargo run
10 4.99 true
```
*What just happened:* You wrote no types, but each value still *has* one - Rust deduced `count` is an
integer, `price` a decimal, and `active` a boolean from their values: static-type safety with dynamic-
language brevity.

When you *do* want to be explicit (or the compiler can't tell), annotate with a colon:

```rust
let count: i64 = 10;
```
*What just happened:* `: i64` says "treat this as a 64-bit integer." You'll write annotations on function
arguments (always required there) and occasionally to pick a specific type; most local `let`s need none.

## The basic types you'll meet first

📝 **Terminology.** A **type** is the kind of thing a value is - what it can hold and what you can do with
it. These are the ones you'll use constantly:

| Type | What it is | Example |
|---|---|---|
| `i32` | A signed integer (whole number, +/−). The default integer. | `let n: i32 = -7;` |
| `u32` | An *unsigned* integer (whole number, 0 and up - no negatives). | `let age: u32 = 30;` |
| `f64` | A 64-bit floating-point number (a decimal). The default float. | `let pi: f64 = 3.14159;` |
| `bool` | A boolean - `true` or `false`. | `let ready: bool = true;` |
| `char` | A single character, in **single** quotes. | `let letter: char = 'R';` |
| `&str` | A string slice - borrowed, read-only text. Literals like `"Ada"` are `&str`. | `let name = "Ada";` |
| `String` | An owned, growable string you can build and modify. | `let mut s = String::new();` |

```rust
fn main() {
    let pi: f64 = 3.14159;
    let ready: bool = true;
    let letter: char = 'R';
    let name = "Ada";
    println!("{pi} {ready} {letter} {name}");
}
```
```console
$ cargo run
3.14159 true R Ada
```
*What just happened:* Four types, each printed with the `{}` placeholder. Note `'R'` uses single quotes
(it's one `char`) while `"Ada"` uses double quotes (it's text, a `&str`).

> ⏭️ The `&str`-vs-`String` distinction confuses *everybody* at first, and it deserves real space - it gets
> that in [Phase 3: Collections](03-collections.md). For now, just know they're two different ways of
> holding text.

## Shadowing - reuse a name with a new value (or type)

Rust lets you write `let` *again* with the same name. This isn't mutation - it creates a brand-new value
that reuses the name. It's called **shadowing**, and it's genuinely useful:

```rust
fn main() {
    let spaces = "   ";        // spaces is text (&str)
    let spaces = spaces.len(); // now spaces is a number (its length)
    println!("{spaces}");
}
```
```console
$ cargo run
3
```
*What just happened:* The second `let spaces` made a *new* `spaces`, even a different type (a number
instead of text). The old one is shadowed (hidden) from that point on. Handy for transforming a value
through a couple of steps while keeping one clear name, instead of inventing `spaces_str`, `spaces_count`,
and so on.

⚠️ **Don't confuse shadowing with `mut`.** `mut` changes the *same* value in place (type must stay the
same). Shadowing makes a *new* value with `let` (type can change). Reassigning without `mut`
(`spaces = ...`) is the error from earlier; re-binding with `let` is shadowing, and is allowed.

## The integer-overflow gotcha

This one catches people, so it's worth meeting now. Integers have a fixed size, so they have a maximum. A
`u8` (8-bit unsigned integer) holds `0` through `255` and nothing larger. What happens if you push past it?

```rust
fn main() {
    let mut x: u8 = 250;
    for _ in 0..10 {
        x += 1;
        println!("{x}");
    }
}
```
```console
$ cargo run
251
252
253
254
255

thread 'main' panicked at src/main.rs:4:9:
attempt to add with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
*What just happened:* `x` climbed to `255` (the max for a `u8`), and the next `+= 1` had nowhere to go, so
the program **panicked**, stopping immediately with `attempt to add with overflow`. A **panic** is Rust's
"I hit an unrecoverable problem, stopping now" - it exits rather than continue with a wrong value.

📝 **Terminology.** A **panic** is a controlled crash: Rust detected something it refuses to continue past
(here, an arithmetic overflow) and halts with a message and a line number instead of silently producing
garbage.

⚠️ **The subtle part: this only panics in debug builds.** `cargo run` (a *debug* build) adds overflow
checks so you catch these during development. In an optimized *release* build (`cargo run --release`),
those checks are off for speed and the value "wraps around" instead (255 + 1 becomes 0). Don't fear
arithmetic - pick an integer type big enough for your values (`i32`/`i64` for general counting are huge),
and know "attempt to add with overflow" means a number outgrew its type.

## Recap

1. **`let` binds a value, immutable by default.** Reassigning it is a compile error.
2. **`mut` opts in to changing** a value (`let mut x = 5;`).
3. **Rust is statically typed with inference** - every value has a type, but you rarely have to write it.
4. The **basic types**: `i32`/`u32` (integers), `f64` (decimals), `bool`, `char` (single quotes), `&str`
   and `String` (text).
5. **Shadowing** re-binds a name with `let` (new value, type may change) - different from `mut`.
6. **Integer overflow panics in debug builds** with "attempt to add with overflow"; pick a big-enough type.

You can hold single values now. Next: *many* values at once - lists, maps, and the two kinds of text Rust
makes you choose between.


---

# Collections

A single value is rarely enough. You'll want a *list* of scores, a *table* of users by name, a *line* of
text built up piece by piece. This phase covers the containers you'll reach for every day, and tackles the
single most confusing thing for Rust newcomers head-on: why there are two kinds of string, `String` and
`&str`, and which to use when.

## `Vec<T>` - the growable list you'll actually use

**What it actually is.** A `Vec` (say "vector") is a list that can grow and shrink. `<T>` means "a Vec of
some type `T`" - `Vec<i32>` is a list of integers, `Vec<String>` a list of strings. Every element is the
same type. This is the workhorse collection; reach for it by default.

```rust
fn main() {
    let mut scores = vec![10, 20, 30];
    scores.push(40);
    println!("{:?}", scores);
    println!("first: {}", scores[0]);
}
```
```console
$ cargo run
[10, 20, 30, 40]
first: 10
```
*What just happened:* `vec![10, 20, 30]` is a macro that builds a `Vec` with three starting values.
`scores.push(40)` added a fourth - why it's `mut`. `scores[0]` reads the first element (counting starts at
`0`). `{:?}` is a new placeholder: `{}` prints for *humans*, `{:?}` prints for *debugging* - handy for
whole collections, which have no "pretty" human form. (Most standard types support it.)

📝 **Terminology.** `{}` is **Display** formatting (clean, for users). `{:?}` is **Debug** formatting (for
you, the programmer). When Rust complains a type "doesn't implement `Display`," try `{:?}` instead.

## Iterating - `for ... in`

To do something with each element, loop over a *reference* to the collection with `for ... in`:

```rust
fn main() {
    let scores = vec![10, 20, 30, 40];
    for s in &scores {
        println!("score: {s}");
    }
}
```
```console
$ cargo run
score: 10
score: 20
score: 30
score: 40
```
*What just happened:* `for s in &scores` walked the list, binding each element to `s` in turn. `&` means
"borrow the list to read it, don't consume it," so `scores` is still usable afterward. (Why `&` matters is
the heart of [Phase 6: Ownership](06-ownership-and-borrowing.md); for now: loop over `&collection` when you
just want to read.)

## Arrays - fixed size, known up front

**What it actually is.** An array is like a `Vec`, but its length is fixed when you write it and never
changes. You'll use these less than `Vec`, but they show up for small, known-size groups of data.

```rust
fn main() {
    let days = [1, 2, 3];
    println!("{:?}", days);
}
```
```console
$ cargo run
[1, 2, 3]
```
*What just happened:* `[1, 2, 3]` is an array of exactly three integers - its size is part of its type
(`[i32; 3]`), and you can't `push` to it. **Rule of thumb:** use `Vec` if the count can change; an array
only when it's truly fixed and small. When in doubt, `Vec`.

## `String` vs `&str` - the confusion, finally cleared up

This is the one. Two types for text, and beginners can never remember which is which. Here's the mental
model that makes it stick:

- **`String` is an owned, growable buffer of text** - *you* own it, it lives on the heap, and you can
  modify and grow it. Think of it as a notebook you bought: yours, and you can keep writing in it.
- **`&str` (a "string slice") is a borrowed *view* into text someone else owns** - read-only, fixed. Think
  of it as a window looking at text: you can read what's there, but not change it through the window. A
  string literal like `"Ada"` is a `&str` (baked into your program - you're just viewing it).

📝 **Terminology.** **Owned** means "this value is responsible for its data and will clean it up."
**Borrowed** (the `&`) means "a temporary reference to data owned elsewhere." This split runs through all
of Rust - strings are just where you meet it first. [Phase 6](06-ownership-and-borrowing.md) makes it the
main event.

Here's both in one place:

```rust
fn main() {
    let mut owned = String::from("Hello"); // owned, growable
    owned.push_str(", world");             // we can grow it
    println!("{owned}");

    let slice: &str = "literal";           // borrowed view, read-only
    println!("{slice}");
}
```
```console
$ cargo run
Hello, world
literal
```
*What just happened:* `String::from("Hello")` made an owned `String` we can extend - `push_str` appended
to it. `"literal"` is a `&str`, a fixed read-only view we can print but not grow. `owned` is a notebook;
`slice` is a window.

### Why two types? And the rule that ends the confusion

Rust splits them because they answer different questions. *Building* text - concatenating, reading user
input, assembling a message - needs ownership and growth, so use `String`. *Looking at* text - passing it
to a function that reads it, comparing it, printing it - doesn't need ownership, so `&str` is lighter and
more flexible.

That leads to the rule that solves 90% of the confusion in real code:

💡 **Key point.** **Take `&str` as a function parameter; return / store `String`.** A function that only
*reads* text should accept `&str`, because then it accepts *both* a borrowed slice and a borrow of an
owned `String` - the most flexible choice. Watch:

```rust
fn greet(name: &str) {        // accepts a view into any text
    println!("Hello, {name}!");
}

fn main() {
    let owned = String::from("world");
    greet("typed inline"); // a &str literal - works
    greet(&owned);         // &owned borrows the String *as* a &str - also works
}
```
```console
$ cargo run
Hello, typed inline!
Hello, world!
```
*What just happened:* `greet` asks for `&str`, and happily took both a literal *and* `&owned` (a borrow of
a `String`). That's the payoff: callers can hand you either kind of text. If `greet` demanded a `String`,
the literal call wouldn't compile and callers would be forced to allocate. So: **read with `&str`, own
with `String`.**

⚠️ **Gotcha.** You can't add a `&str` to a `String` with `+` the way you might guess from other languages,
and comparing a `String` to a `&str` needs care. Until ownership clicks, lean on the methods:
`my_string.push_str("more")` to append, `String::from("text")` to make one, and `&my_string` to pass it
where a `&str` is wanted. These cover almost everything early on.

## `HashMap` - look things up by key

**What it actually is.** A `HashMap<K, V>` stores **key → value** pairs and looks up a value by key fast -
the "dictionary" / "associative array" from other languages: an age by a person's name, a price by a
product code, a count by a word.

Unlike `Vec`, `HashMap` isn't in scope automatically - bring it in with a `use` line at the top of the file
(more on `use` in [Phase 5](05-modules-and-project-layout.md)):

```rust
use std::collections::HashMap;

fn main() {
    let mut ages: HashMap<String, i32> = HashMap::new();
    ages.insert(String::from("Ada"), 36);
    ages.insert(String::from("Linus"), 54);

    match ages.get("Ada") {
        Some(age) => println!("Ada is {age}"),
        None => println!("Ada not found"),
    }
}
```
```console
$ cargo run
Ada is 36
```
*What just happened:* We created an empty map from `String` keys to `i32` values, inserted two pairs, then
looked one up. `ages.get("Ada")` doesn't return an age directly, but an `Option`: `Some(age)` if the key
exists, `None` if it doesn't. Rust makes you handle the "missing key" case right there with `match`, so you
can never accidentally use a value that wasn't found.

> ⏭️ `Option`, `Some`, `None`, and `match` are coming up properly: `match` in
> [Phase 4](04-control-flow-and-functions.md), and `Option`'s "no null in Rust" story in
> [Phase 7](07-errors-and-io.md). For now: `get` returns `Some(value)` or `None`, and you handle both.

⚠️ **Gotcha - HashMaps have no order.** Iterate a `HashMap` and the pairs come out in an unpredictable
order that can differ run to run - that's by design, and what makes lookups fast. Need a stable order?
Sort the keys yourself, or reach for `BTreeMap` (a sorted map) instead.

## Recap

1. **`Vec<T>`** is the growable list - your default container; `vec![...]` builds one, `.push()` grows it.
2. **`for x in &collection`** iterates by borrowing (so the collection survives the loop).
3. **Arrays** (`[1, 2, 3]`) are fixed-size; use `Vec` when the count can change.
4. **`String`** is owned and growable; **`&str`** is a borrowed, read-only view. **Read with `&str`, own
   with `String`.**
5. **`HashMap<K, V>`** stores key → value; `.get(key)` returns `Some(value)` or `None`, and it has no
   guaranteed order.
6. **`{}`** prints for humans (Display); **`{:?}`** prints for debugging (Debug) - use the latter for whole
   collections.

You can store data now. Next: make decisions about it and bundle logic into functions, where you'll meet
`match`, the feature Rust programmers love most.


---

# Control Flow & Functions

So far your programs run straight down, top to bottom. Real programs branch ("if it's hot, say so"),
repeat ("for each score, print it"), and split work into named, reusable pieces (functions). Along the way
you'll meet two ideas that make Rust feel different from most languages: **almost everything is an
expression that produces a value**, and **`match` forces you to handle every possible case.** That second
one is the star of the phase - the feature Rust programmers miss most when they go back to other languages.

## `if` / `else` - and it's an expression

You know `if`/`else`. The twist in Rust: an `if` doesn't just *do* something, it *produces a value*, so you
can assign its result directly to a variable.

```rust
fn main() {
    let temp = 30;
    let label = if temp > 25 { "hot" } else { "mild" };
    println!("{label}");
}
```
```console
$ cargo run
hot
```
*What just happened:* `if temp > 25 { "hot" } else { "mild" }` *evaluated* to one of the two strings and
`let label =` caught it. There's no ternary `? :` in Rust because plain `if` already does that job. (Both
branches must produce the *same* type.)

📝 **Terminology.** An **expression** produces a value (`2 + 2`, `if ... { } else { }`). A **statement**
does something but produces no usable value (`let x = 5;`). Rust leans hard on expressions - keep this
distinction in mind; it explains how functions return, below.

## The loops: `for`, `while`, `loop`

Rust has three ways to repeat, each for a different shape of problem.

**`for ... in`** - walking over a collection or a range. This is the one you'll use most:

```rust
fn main() {
    for n in [10, 20, 30] {
        println!("{n}");
    }
    for i in 0..3 {
        println!("i = {i}");
    }
}
```
```console
$ cargo run
10
20
30
i = 0
i = 1
i = 2
```
*What just happened:* The first loop walked the array. The second walked a **range**, `0..3`, meaning "0 up
to but not including 3" - so `0, 1, 2`. (To include the end, use `0..=3` for `0, 1, 2, 3`.)

**`while`** - repeats as long as a condition holds:

```rust
fn main() {
    let mut count = 0;
    while count < 3 {
        println!("count {count}");
        count += 1;
    }
}
```
```console
$ cargo run
count 0
count 1
count 2
```
*What just happened:* The loop ran while `count < 3`, printing and incrementing each pass, then stopped
once `count` reached `3`. (Forgetting `mut` on `count` bites people here, as warned in
[Phase 2](02-syntax-values-and-types.md).)

**`loop`** - repeats forever until you `break` out. Useful when the exit condition is in the middle, not
the top:

```rust
fn main() {
    let mut n = 1;
    loop {
        if n > 3 {
            break;
        }
        println!("n = {n}");
        n += 1;
    }
}
```
```console
$ cargo run
n = 1
n = 2
n = 3
```
*What just happened:* `loop` runs unconditionally; `break` is the only way out (here, once `n > 3`). Reach
for it when "keep going until something happens" reads more naturally than a `while` condition up top.

## `match` - the star: handle every case, exhaustively

**What it actually is.** `match` compares a value against a list of patterns and runs the first one that
fits. It's like a `switch` from other languages, but with a superpower: **it must cover every possible
case.** Forget one and the program *won't compile* - sounds strict, but it's one of Rust's best
bug-prevention features.

```rust
fn classify(n: i32) -> &'static str {
    match n {
        0 => "zero",
        1 | 2 | 3 => "small",
        4..=9 => "medium",
        _ => "large",
    }
}

fn main() {
    for n in [0, 2, 7, 100] {
        println!("{n} is {}", classify(n));
    }
}
```
```console
$ cargo run
0 is zero
2 is small
7 is medium
100 is large
```
*What just happened:* Each **arm** (`pattern => result`) is checked top to bottom; the first match wins.
`1 | 2 | 3` matches any of those values; `4..=9` matches the inclusive range 4 through 9; `_` is the
catch-all. Like `if`, `match` is an *expression*, which is why `classify` can hand the whole `match` back
as its answer.

Here's the flow of evaluating a single value through that `match`:

```mermaid
flowchart TD
  V[value n] --> A{n == 0?}
  A -- yes --> R0["zero"]
  A -- no --> B{n is 1, 2, or 3?}
  B -- yes --> R1["small"]
  B -- no --> C{n in 4..=9?}
  C -- yes --> R2["medium"]
  C -- no --> R3["large (_)"]
```

### Why "exhaustive" is a gift, not a chore

Say you have a type with a fixed set of options (an `enum` - a value that's exactly one of several named
variants), and you forget to handle one:

```rust
enum Light { Red, Yellow, Green }

fn action(l: Light) -> &'static str {
    match l {
        Light::Red => "stop",
        Light::Green => "go",
        // forgot Yellow!
    }
}
```
```console
$ cargo run
error[E0004]: non-exhaustive patterns: `Light::Yellow` not covered
 --> src/main.rs:4:11
  |
4 |     match l {
  |           ^ pattern `Light::Yellow` not covered
  |
note: `Light` defined here
 --> src/main.rs:1:6
  |
1 | enum Light { Red, Yellow, Green }
  |      ^^^^^        ------ not covered
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
```
*What just happened:* The compiler **refused to build** because `Light::Yellow` isn't handled. In most
languages this would compile fine and quietly do nothing for yellow lights, a bug you'd find in production.
Rust catches it at compile time and names exactly which case you missed.

💡 **Key point.** Exhaustive `match` means: add a new variant to an enum later, and the compiler walks you
to *every* `match` that now needs updating. "Handle all the cases" stops being something you remember - the
compiler remembers for you. Why refactoring Rust feels safe.

⚠️ **Gotcha - `_` can hide bugs.** The catch-all `_` *also* satisfies exhaustiveness. Perfect for
genuinely-infinite types like `i32` (as in `classify`), but on an enum it switches off the helpful "you
forgot a case" check. So on enums, prefer listing variants explicitly when you reasonably can - you *want*
the compiler to nag you when a new variant appears.

## Functions - and returning without `return`

**What it actually is.** A function is a named, reusable block of logic that takes inputs (parameters) and
optionally produces an output. Define one with `fn`, name the parameter types (always required), and name
the return type after `->`.

The Rust-flavored part: a function returns the value of its **last expression**, which has **no
semicolon.**

```rust
fn square(x: i32) -> i32 {
    x * x
}

fn main() {
    println!("square of 5 is {}", square(5));
}
```
```console
$ cargo run
square of 5 is 25
```
*What just happened:* `square` takes an `i32` named `x` and returns an `i32` (`-> i32`). Its body is the
single expression `x * x` - **no semicolon** - so that value *becomes the return value*. You don't need to
write `return x * x;`, though you can; `return` exists mainly for returning early mid-function.

⚠️ **The semicolon gotcha that bites everyone once.** A semicolon turns an expression into a statement,
which produces *no value*. Writing `x * x;` as the last line means the function returns "nothing" - and if
you promised an `i32`, the compiler stops you with a "mismatched types" error, expecting `i32` but finding
`()` (Rust's "no value," the *unit type*). The fix is almost always: **delete the trailing semicolon.**
Once "last line, no semicolon = the return value" clicks, this stops happening.

📝 **Terminology.** `()` is the **unit type** - Rust's way of saying "no meaningful value." A function with
no `-> Type` returns `()` (it's run for its side effects, like printing).

## Recap

1. **`if`/`else` is an expression** - it produces a value you can assign; there's no ternary because `if`
   covers it.
2. **`for ... in`** walks collections and ranges (`0..3` excludes the end; `0..=3` includes it); **`while`**
   loops on a condition; **`loop`** runs until `break`.
3. **`match` compares a value to patterns and is *exhaustive*** - forget a case and it won't compile. Like
   `if`, it's an expression that produces a value.
4. Exhaustiveness is a **gift**: add an enum variant and the compiler points you to every `match` to fix.
5. **Functions** use `fn`, require parameter types, and return after `->`. The **last expression with no
   semicolon** is the return value - a stray semicolon there is the classic beginner bug.

You can now branch, loop, and factor logic into functions. As programs grow past one file, you'll need a
way to organize them - that's next: modules, crates, and how a real Rust project is laid out. Right after
that comes the phase everything has been building toward: ownership.


---

# Modules & Project Layout

Everything so far lived in one `main.rs`. Real projects don't - they grow into many files, pull in code
other people wrote, and split logic into tidy named groups. This phase is the map: what each file in a
Cargo project is *for*, how to carve code into **modules**, what `pub` and `use` actually do, and what
people mean by a **crate**. None of this is hard - it's mostly naming things you've half-noticed already.

This is also the last phase before the big one: [Phase 6: Ownership](06-ownership-and-borrowing.md), the
phase the whole guide has been walking toward.

## Anatomy of a Cargo project

Remember what `cargo new hello` made back in [Phase 1](01-install-and-first-program.md)? Let's read it now
that the pieces will mean something:

```console
$ ls hello
Cargo.toml  src
$ ls hello/src
main.rs
```

Two things matter:

**`Cargo.toml` - the manifest.** A small config file describing your project: name, version, and the
libraries it depends on. You'll edit this whenever you add a dependency.

```toml
[package]
name = "hello"
version = "0.1.0"
edition = "2024"

[dependencies]
```
*What just happened:* `[package]` names your project and pins the **edition** (a several-year batch of
language conventions - `2021` and `2024` are both fine; `cargo new` picks a recent one for you).
`[dependencies]` is empty for now - where added libraries get listed.

📝 **Terminology.** `Cargo.toml` is the file *you* edit. You'll also see a `Cargo.lock` appear next to it -
written by `cargo` to record the *exact* versions it resolved, so builds are reproducible. Leave
`Cargo.lock` to `cargo`; don't hand-edit it.

**`src/main.rs` vs `src/lib.rs` - the two kinds of crate root.** Worth memorizing:

- **`src/main.rs`** is the entry point of a **binary** - a program you *run*. It has a `fn main()`. This is
  what `cargo new` gives you by default.
- **`src/lib.rs`** is the entry point of a **library** - code meant to be *used by other code*, not run on
  its own. It has no `main`. You get one with `cargo new --lib mylib`.

💡 **Key point.** A binary is a thing you run; a library is a thing you reuse. Many real projects have
both: a `lib.rs` holding the actual logic (easy to test and share) and a thin `main.rs` that calls into it.
For now, `main.rs` is all you need.

## `mod` - group code into modules

**What it actually is.** A **module** is a named container for related code - functions, types, other
modules. It's how you keep a growing file from becoming a wall of unrelated functions. Declare one with
`mod`.

```rust
mod greetings {
    pub fn hello(name: &str) -> String {
        format!("Hello, {name}!")
    }
}

fn main() {
    println!("{}", greetings::hello("Ada"));
}
```
```console
$ cargo run
Hello, Ada!
```
*What just happened:* `mod greetings { ... }` created a module holding one function. From outside, reach
into it with `greetings::hello(...)` - `::` is the path separator, "the `hello` inside `greetings`."
(`format!` is like `println!` but builds a `String` instead of printing it.)

📝 **Terminology.** A **path** like `greetings::hello` names *where* something lives - module by module,
separated by `::`. You've already used paths: `std::collections::HashMap` in [Phase 3](03-collections.md)
is "`HashMap`, inside `collections`, inside the standard library `std`."

## `pub` - make things public

Did you notice the `pub` on `hello`? That's not decoration. **By default, everything in a module is
private - usable only inside that module.** `pub` ("public") opens it up to the outside.

```rust
mod greetings {
    pub fn hello(name: &str) -> String {
        format!("Hello, {name}!")
    }

    fn secret() -> &'static str {  // no pub → private
        "private"
    }
}

fn main() {
    println!("{}", greetings::hello("Ada"));
    println!("{}", greetings::secret());  // try to use the private one
}
```
```console
$ cargo run
error[E0603]: function `secret` is private
  --> src/main.rs:13:31
   |
13 |     println!("{}", greetings::secret());
   |                               ^^^^^^ private function
   |
note: the function `secret` is defined here
  --> src/main.rs:6:5
   |
 6 |     fn secret() -> &'static str {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
```
*What just happened:* `hello` is `pub`, so calling it works. `secret` has no `pub`, so it's private to the
`greetings` module, and the compiler **refuses** to let `main` reach in and call it. Code *inside*
`greetings` could still call `secret` freely; only the outside is blocked.

💡 **Key point.** Private-by-default is the same philosophy as immutable-by-default from
[Phase 2](02-syntax-values-and-types.md): Rust makes the safe, restrictive choice the default and asks you
to opt *out*. The benefit is a clear contract - anyone reading your module sees exactly which functions are
meant to be used and which are internal plumbing they shouldn't depend on.

## `use` - bring a name into scope

Typing `greetings::hello` every time gets old, and long paths like `std::collections::HashMap` get *very*
old. `use` pulls a name into the current scope so you can refer to it by its short name:

```rust
mod greetings {
    pub fn hello(name: &str) -> String {
        format!("Hello, {name}!")
    }
}

use greetings::hello;  // bring `hello` into scope

fn main() {
    println!("{}", hello("Ada"));  // now just `hello`, no path
}
```
```console
$ cargo run
Hello, Ada!
```
*What just happened:* `use greetings::hello;` made `hello` available by its bare name in `main`. Same
reason you wrote `use std::collections::HashMap;` before using `HashMap` in [Phase 3](03-collections.md) -
`use` shortens the path; it doesn't change what the thing is.

Modules can nest, giving your project a tree you can picture:

```mermaid
flowchart TD
  Root[crate root: main.rs] --> G[mod greetings]
  Root --> M[mod math]
  G --> H["pub fn hello"]
  G --> S["fn secret (private)"]
  M --> A["pub fn add"]
  M --> P["mod private_helpers"]
```

*Reading the tree:* the crate root (`main.rs`) holds two modules; `greetings` exposes `hello` but keeps
`secret` private; `math` exposes `add` and has its own nested module. A path like `math::add` walks this
tree from the root down.

## Crates and dependencies

📝 **Terminology.** A **crate** is the unit Rust compiles: one binary or one library. Your project is a
crate, and the libraries you pull in are crates too - published on
**[crates.io](https://crates.io)**, Rust's package registry (like npm for JavaScript or PyPI for Python).

To use someone else's crate, add it to `Cargo.toml` - easiest via `cargo add`, which edits the file for
you:

```console
$ cargo add rand
    Updating crates.io index
      Adding rand v0.9.2 to dependencies
```
*What just happened:* `cargo add rand` looked up the `rand` crate (random numbers), wrote it into
`[dependencies]`, and noted the version. The next `cargo build` or `cargo run` downloads and compiles it
automatically - that's the build whose *first* run is a bit slow, as flagged in
[Phase 1](01-install-and-first-program.md). After that, bring its items in with `use` (e.g.
`use rand::Rng;`) just like your own modules. That's the whole point of `cargo`: this short.

⚠️ **Gotcha - `mod` vs `use` confusion.** These do *different* jobs. `mod foo;` *declares that a module
exists* (and, in a multi-file project, tells Rust to load `foo.rs`). `use foo::bar;` *brings an existing
name into scope* for convenience. Declare a module **once** with `mod`; `use` names from it as often as
you like. "Unresolved import" usually means a `use` for something never `mod`-declared (or never `pub`).

## You're ready for the hard part

You can now write Rust that holds data, makes decisions, lives in functions, and is organized into modules
across a real project - a genuine working foundation, most of a programming language.

But there's one idea we've been circling: every time you saw a `&` (borrowing a `Vec` in a `for` loop,
passing `&owned` where a `&str` was wanted, `&str` itself), a deeper system was at work. That system is
**ownership**, the reason Rust can promise memory safety without a garbage collector.

🪖 **A word before you turn the page.** Phase 6 is *the* phase. It's where Rust stops resembling languages
you know and the borrow checker - the part of the compiler that enforces ownership - starts rejecting code
that looks fine to you. Everyone struggles here; it's the normal shape of learning Rust, not a sign you're
doing badly. Take it slowly, type the examples, and let the mental model build - once ownership clicks,
the rest of Rust falls into place around it. For the "why does any language need this?" background first,
see [Memory & Garbage Collection](/guides/memory-and-garbage-collection).

## Recap

1. **`Cargo.toml`** is the manifest you edit (name, edition, dependencies); **`Cargo.lock`** is managed by
   `cargo`.
2. **`src/main.rs`** is a binary you run (`fn main`); **`src/lib.rs`** is a library other code reuses.
3. **`mod`** groups code into modules; reach into them with `::` paths.
4. **Everything is private by default**; **`pub`** exposes it. Privacy is a deliberate, opt-out contract.
5. **`use`** shortens a path by bringing a name into scope - it doesn't change what the thing is.
6. A **crate** is a compiled unit (your project, or a library); **`cargo add <name>`** pulls one from
   crates.io.

Next: the heart of Rust - ownership and borrowing. Who owns a value, what "moving" and "borrowing" really
mean, and why the borrow checker is on your side even when it's saying no.


---

# Ownership & Borrowing - Rust's Big Idea

This is the phase people warn you about. Maybe someone already told you that Rust has a "borrow checker" that will yell at you, and you're bracing for a fight. Here's the reframe that changes everything: **the borrow checker is not your enemy. It's a teacher who refuses to let you ship a whole class of bug.** Every error it gives you is it saying, "Wait - this would have crashed or corrupted memory at runtime. Let's fix it now, on your screen, instead of at 2am in production."

Once the rules click, they stop feeling like rules and start feeling like common sense. So we'll install the mental model first, slowly, and only then look at the famous errors - and you'll find they suddenly read as helpful.

If you've ever wrestled with `null`, dangling pointers, or use-after-free bugs in another language - or read [How Memory & Garbage Collection Work](/guides/memory-and-garbage-collection) - this phase is the punchline to that story: Rust solves those problems *at compile time*, with no garbage collector running in the background.

## The three rules of ownership

Everything in this phase grows from three short rules. Read them once; you'll recognize them when they show up.

1. **Each value has exactly one owner** - a single variable that's responsible for it.
2. **There can only be one owner at a time.** When you give the value to someone else, the old owner gives it up.
3. **When the owner goes out of scope, the value is dropped** - its memory is freed automatically, right then.

📝 **Terminology.** *Scope* is the region of code where a variable is valid - usually between its `let` and the closing `}` of the block it lives in. *Dropped* means Rust runs the value's cleanup and frees its memory. You never call `free` yourself; rule 3 does it for you.

That third rule is the quiet miracle: no garbage collector pausing your program to hunt for unused memory, no `free()` to forget. The compiler knows exactly where each value's owner ends, so it knows exactly where to insert the cleanup. **Memory safety, decided at compile time.**

## Move semantics: giving a value away

**What it actually is.** In most garbage-collected languages (Python, Java, JavaScript), `let t = s` copies a reference and both names point at the same thing. In Rust, for a value that owns heap memory (like a `String`), `let t = s` *moves* ownership: `t` becomes the owner, and `s` is no longer valid. The value wasn't copied - it changed hands.

**Why this exists.** If both `s` and `t` owned the same `String`, then when both went out of scope, Rust would try to free the same memory twice - a "double free," a classic crash. Making the assignment a move guarantees there's always exactly one owner to do the cleanup. Rule 2, enforced.

**Why people get this wrong.** They expect `s` to still work after `let t = s`, because that's how nearly every other language behaves. Let's watch Rust stop us, and read what it says.

```rust
fn main() {
    let s = String::from("hello");
    let t = s;            // ownership moves from s to t
    println!("{}", s);    // ...but we try to use s anyway
}
```
```console
$ cargo build
error[E0382]: borrow of moved value: `s`
 --> src/main.rs:4:20
  |
2 |     let s = String::from("hello");
  |         - move occurs because `s` has type `String`, which does not implement the `Copy` trait
3 |     let t = s;
  |             - value moved here
4 |     println!("{}", s);
  |                    ^ value borrowed here after move
  |
help: consider cloning the value if the performance cost is acceptable
  |
3 |     let t = s.clone();
  |              ++++++++
```
*What just happened:* Read that error like a sentence, because it *is* one. "move occurs because `s` has type `String`" - assigning a `String` is a move. "value moved here" points at `let t = s`. "value borrowed here after move" points at exactly the line where you tried to use `s` again. The compiler even drew arrows to the two conflicting lines. This isn't a cryptic stack trace - it's a code review that found a bug before the program ever ran.

💡 **Key point.** Passing a value into a function moves it too, by the same rule. `do_thing(s)` hands ownership of `s` to `do_thing`; afterward, `s` is gone from your scope unless the function hands it back. Assigning, passing, or returning are all the same move.

⚠️ **The "just clone it" trap.** The error helpfully suggests `s.clone()`, which makes a full independent copy so both names own their own data. Sometimes that's exactly right. But reaching for `.clone()` *every* time the borrow checker complains is the most common beginner habit, and it quietly copies data all over your program. Treat clone as a deliberate choice ("I genuinely need two copies"), not a reflex to silence the compiler. Most of the time, the better answer is the next idea: **borrowing.**

## Borrowing: using a value without taking it

**What it actually is.** A *borrow* is a reference to a value you can use without owning it. Write `&value` to borrow it. The owner keeps ownership; you get temporary access - like reading a book off a friend's shelf instead of making them give you the book.

This is how you call a function on a value and *still have it afterward*:

```rust
fn length(s: &String) -> usize {
    s.len()              // we can read s through the borrow
}

fn main() {
    let s = String::from("hello");
    let n = length(&s);  // lend s to length, don't give it away
    println!("{} is {} chars", s, n);   // s is still ours!
}
```
```console
$ cargo run
hello is 5 chars
```
*What just happened:* `&s` handed `length` a *reference* to `s` instead of moving the `String` itself. `length` read it and returned, the borrow ended, and `s` was still fully ours on the next line - no move, no clone, no copy of the string's bytes, just a temporary loan.

📝 **Terminology.** `&T` is a *shared reference* (also called an *immutable borrow*) - you can read through it but not change the value. `&mut T` is a *mutable reference* (an *exclusive borrow*) - you can change the value through it.

## The one rule that governs all borrowing

Here is the single most important rule in the language, and it's short:

> **At any given time, you can have *either* one mutable reference *or* any number of shared references - but not both.**

Read it as: *many readers, or one writer, never both at once.* If something can be changed (a `&mut`), nothing else may be looking at it. If many things are looking at it (`&`), nobody may change it underneath them.

**Why this exists.** This rule kills data races and a whole family of aliasing bugs. If a value can't be mutated while anything else holds a view of it, no reader can ever be surprised by the value changing out from under them. The compiler proves this for you. Let's see it catch a violation:

```rust
fn main() {
    let mut v = vec![1, 2, 3];
    let a = &mut v;          // first exclusive borrow
    let b = &mut v;          // second exclusive borrow - not allowed
    println!("{:?} {:?}", a, b);
}
```
```console
$ cargo build
error[E0499]: cannot borrow `v` as mutable more than once at a time
 --> src/main.rs:4:13
  |
3 |     let a = &mut v;
  |             ------ first mutable borrow occurs here
4 |     let b = &mut v;
  |             ^^^^^^ second mutable borrow occurs here
5 |     println!("{:?} {:?}", a, b);
  |                           - first borrow later used here
```
*What just happened:* The compiler caught two `&mut` borrows of `v` alive at the same time and refused. Again it's precise: it underlines the *first* mutable borrow, the *second* one, and the line where the first is *later used* (why the first one was still alive when the second appeared). The fix is usually to not need both at once - finish using `a` before you make `b`, or restructure so only one writer exists.

🪖 **War story.** A teammate coming from Python spent an afternoon furious at E0499 in a loop that mutated a list while iterating over it. Then it dawned on him: the same code in Python had been silently producing wrong results for months, because mutating a collection mid-iteration is a real bug - Python just never complained. Rust wasn't being difficult. It had found a bug hiding in plain sight.

## Move vs. borrow, at a glance

When you hand a value to a function (or another variable), you're choosing one of three things - a decision you'll make hundreds of times a day, until it becomes automatic:

```mermaid
flowchart TD
  Q{Need to use the<br/>value again after?}
  Q -- No --> M[Move it: pass `value`]
  Q -- Yes, only read it --> S[Share-borrow: pass `&value`]
  Q -- Yes, and change it --> X[Mut-borrow: pass `&mut value`]
  X --> R[Rule: no other borrow<br/>may exist meanwhile]
  S --> R2[Rule: many `&` ok,<br/>but no `&mut` meanwhile]
```

Most of the time the answer is "borrow" (`&`), because you usually want to keep using your values. Reach for a move when you're genuinely done with the value, and `&mut` when you need to change something in place.

## A taste of lifetimes

You'll eventually meet syntax like `&'a str` with that little `'a`, and it can look intimidating. Here's the whole idea in one sentence so it's not scary when it shows up:

**A lifetime is the compiler's name for "how long a reference is valid."** It exists to guarantee a reference never outlives the thing it points to - that's how Rust makes dangling pointers *impossible* instead of merely unlikely.

Most of the time the compiler figures lifetimes out silently and you never type one. You only write them down in the few cases it can't infer the relationship on its own - for example, a function returning a reference that needs to say *which* input it borrows from. When you get there, remember: you're not learning a new feature, just spelling out a "this reference can't outlive that value" relationship the compiler was already enforcing. Don't let `'a` intimidate you out of the gate.

## How this gives you safety with no GC and no `free`

Step back and look at what these rules bought you:

- **No double-free**, because every value has exactly one owner to clean it up (move semantics).
- **No use-after-free / dangling pointers**, because a reference can never outlive its owner (borrowing + lifetimes).
- **No data races from aliasing**, because you can't mutate something while it's shared (the one-writer-or-many-readers rule).
- **No manual `free()` to forget**, because cleanup happens automatically when the owner's scope ends (rule 3).
- **No garbage collector**, because the compiler already knows the exact moment each value dies - no runtime needed to go find out.

Languages with a garbage collector (covered in [How Memory & Garbage Collection Work](/guides/memory-and-garbage-collection)) buy safety by running a collector that periodically pauses your program to figure out what's still in use. Rust buys the *same* safety by proving it at compile time instead. The trade is real, no sugarcoating: more thinking up front (and sometimes arguing with the borrow checker), in exchange for no GC pauses and predictable, instant cleanup. That trade-off - and how different languages make it - is exactly the kind of design decision [Programming Languages, Explained Like a Human](/guides/languages-explained-like-a-human) walks through.

## When you're "fighting the borrow checker"

You will hit a wall where the compiler rejects something you're *sure* is fine, and it'll feel personal. Two things to hold onto:

1. **It's almost always pointing at a real issue** - some way the value's lifetime or aliasing doesn't line up. The fix is usually to restructure (borrow instead of move, narrow a scope, finish using one borrow before starting another), not to fight harder.
2. **Resist the clone-to-escape reflex.** Sprinkling `.clone()` to make errors go away works, but it's how you end up with slow, copy-everything code. Clone when you *mean* to copy; otherwise, listen to what the error is telling you about your data's shape.

Here's the genuinely encouraging part: **the fights get rare fast.** The first week you argue with the borrow checker constantly. By the second or third, you start writing code it accepts on the first try, because you've absorbed the model - you're now thinking about ownership the way it does. It stops feeling like a checker in your way and starts feeling like a habit you already have.

## Recap

1. **Three rules:** each value has one owner; only one owner at a time; the value is dropped (freed) when the owner's scope ends.
2. **Move:** assigning, passing, or returning a heap-owning value *moves* ownership; the old name is no longer valid (`error[E0382]: borrow of moved value`).
3. **Borrow:** use a value without owning it - `&` for shared/read, `&mut` for exclusive/write.
4. **The borrowing rule:** many `&` *or* one `&mut`, never both at once (`error[E0499]` when you break it). Many readers, or one writer.
5. **Lifetimes** are just the compiler's name for "how long a reference is valid" - they make dangling pointers impossible.
6. Together these give **memory safety with no garbage collector and no manual `free`** - checked at compile time. The borrow checker is a teacher; the fights get rare fast.


---

# Errors & I/O - Failure in the Type System

In a lot of languages, errors are a surprise. A function looks like it returns a number, and then one day it throws an exception you never saw coming, unwinding your program through five layers of code you forgot existed. The frightening part isn't that things fail - everything fails sometimes - it's that the failure was *invisible* until it happened.

Rust makes a different bet, and it's the heart of this phase: **failure and absence are written into the type.** If a function can fail, its return type says so, out loud, and the compiler won't let you ignore it. There are no hidden exceptions to ambush you - error handling stops being a chore bolted on at the end and becomes part of how you read code.

## The two types that change how you think

Almost all of Rust's error story is two enums from the standard library. Learn these two shapes and you've learned the mental model.

**`Option<T>` - "a `T`, or nothing."** Use it when a value might legitimately be absent. Exactly two cases:

- `Some(value)` - there's a value, here it is.
- `None` - there's nothing.

This is Rust's answer to `null`. Instead of every value secretly maybe being null (and blowing up when you forget to check), *only* `Option` values can be absent, and the type forces you to handle `None`. The billion-dollar mistake, designed out.

**`Result<T, E>` - "a `T` if it worked, or an error `E` if it didn't."** Use it when an operation can fail. Two cases:

- `Ok(value)` - success, here's the result.
- `Err(error)` - failure, here's what went wrong.

📝 **Terminology.** Both are *enums* - types that are exactly one of a fixed set of variants (enums proper come in [Phase 9](09-idioms-and-gotchas.md)). `<T>` and `<E>` are *generics*: placeholders for "whatever type this holds." `Option<String>` is "a String or nothing"; `Result<u64, std::io::Error>` is "a u64, or an I/O error."

💡 **Key point.** The whole philosophy in one line: **a function's type tells you how it can fail.** You never have to guess or read docs to find out - the return type already told you.

## Handling them with `match`

The most direct way to deal with an `Option` or `Result` is `match`, which forces you to handle *every* case - the compiler won't compile a `match` that forgets one. That exhaustiveness is the safety net.

```rust
fn main() {
    let text = "42";
    let parsed = text.parse::<i32>();   // parse returns Result<i32, _>

    match parsed {
        Ok(n) => println!("got the number {}", n),
        Err(e) => println!("couldn't parse: {}", e),
    }
}
```
```console
$ cargo run
got the number 42
```
*What just happened:* `parse` returns a `Result<i32, ...>`, not a plain `i32`, because the string might not be a number. `match` pulls the value out of whichever case happened: `Ok(n)` binds the parsed number to `n`; `Err(e)` binds the error to `e`. There's no way to use the number without acknowledging parsing could fail.

Change `"42"` to `"oops"` and you'd hit the `Err` arm with a message like `invalid digit found in string`. Same code, both paths handled.

## The `?` operator: handle-or-bubble-up, in one character

Writing a full `match` for every fallible call gets verbose fast, especially when you just want "if this failed, stop and pass the error up to my caller." That's so common Rust gives it a one-character shortcut: **`?`**.

Put `?` after a `Result` (or `Option`) and it means: *if `Ok`, unwrap the value and keep going; if `Err`, return that error from the current function right now.*

```rust
use std::fs;

fn read_config() -> Result<String, std::io::Error> {
    let contents = fs::read_to_string("config.toml")?;   // ? here
    Ok(contents)
}

fn main() {
    match read_config() {
        Ok(text) => println!("config is {} bytes", text.len()),
        Err(e) => println!("could not read config: {}", e),
    }
}
```
```console
$ cargo run
could not read config: The system cannot find the file specified. (os error 2)
```
*What just happened:* `fs::read_to_string` returns a `Result<String, std::io::Error>`. The `?` says "give me the `String` if it worked; otherwise return the `io::Error` from `read_config` immediately." Because the file didn't exist, the error bubbled straight up to `main`, where the `match` printed it. The happy path stays clean: `let contents = fs::read_to_string(...)?;` reads like ordinary code.

⚠️ **`?` needs a compatible return type.** You can only use `?` inside a function that itself returns a `Result` (or `Option`) the error can fit into, because `?`'s job is to *return early* with that error. Use it in a function returning a plain value and you'll get a compile error - that's why `read_config` returns `Result<String, ...>` and not just `String`.

## `panic!` vs. recoverable errors

So far everything has been *recoverable* - the caller gets a `Result` and decides what to do. Rust has a second, blunter mechanism for the other situation: **`panic!`**, for when the program has hit a state with no sane way to continue (a bug, a broken invariant, something that should be *impossible*).

A panic unwinds the current thread, prints a message, and (for a normal program) exits. It's not for "the file wasn't there" - that's expected and recoverable. It's for "I assumed this list had at least one element and it's empty, so my logic is wrong."

The mental split:

- **Recoverable** (caller can reasonably handle it): return a `Result` or `Option`. Missing file, bad input, network timeout.
- **Unrecoverable** (a bug, no sensible recovery): `panic!`. Broken invariant, "this can't happen," an index out of bounds.

## `.unwrap()` - the convenient little landmine

`Result` and `Option` both have an `.unwrap()` method: "give me the value inside, and if it's `Err`/`None`, just **panic**." It's the shortcut everyone learns on day one - genuinely useful, and genuinely dangerous in the wrong place.

```rust
fn main() {
    let f = std::fs::File::open("nope.txt").unwrap();
    println!("{:?}", f);
}
```
```console
$ cargo run
thread 'main' panicked at src/main.rs:2:45:
called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
*What just happened:* The file didn't exist, so `File::open` returned `Err(...)`. `.unwrap()` looked inside, found an `Err`, and did the only thing it knows how: panic and crash the program. The message gives you the exact line and OS error - useful, but your program is *dead*. In a real service, that's an outage.

⚠️ **When `.unwrap()` is okay vs. not.** Be straight with yourself every time you type it:

- ✅ **Fine** in quick experiments, throwaway scripts, examples, and tests, where crashing on failure is exactly what you want and clutter would hurt clarity.
- ✅ **Fine** when failure is *truly* impossible and you can prove it (e.g. parsing a hard-coded constant string you wrote yourself). Even then, prefer `.expect("reason this can't fail")` so the panic message explains your assumption.
- ❌ **Not okay** in real application or library code on anything that can fail in production - file reads, network calls, user input, parsing data from elsewhere. There, every `.unwrap()` is a crash waiting for a bad day. Use `?` to bubble the error up, or `match` to handle it.

🪖 **War story.** A CLI tool shipped with `config.parse().unwrap()` on the user's config file. It worked flawlessly - until a user added a stray comma. Instead of "line 12: unexpected comma," they got a raw panic and a backtrace, and filed a bug saying the tool "randomly crashes." One `.unwrap()` swapped for a `match` turned a scary crash into a friendly message. The failure was always coming; `.unwrap()` just made it ugly.

## Reading a file, the everyday way

You've seen the pieces already; here's the complete, idiomatic shape for "read a file and do something with it," using `?` to keep it clean:

```rust
use std::fs;

fn main() -> Result<(), std::io::Error> {
    let contents = fs::read_to_string("notes.txt")?;
    let lines = contents.lines().count();
    println!("notes.txt has {} lines", lines);
    Ok(())
}
```
```console
$ cargo run
notes.txt has 7 lines
```
*What just happened:* Two details worth noticing. First, `main` returns `Result<(), std::io::Error>` - yes, `main` can return a `Result`, which is what lets us use `?` right inside it. Second, `Ok(())` is how a `Result`-returning function says "success, no meaningful value" - `()` is the empty "unit" type. If the file were missing, `?` would return the `Err` from `main`, exiting with that error printed and a non-zero status - no panic, just a clean failure.

## Recap

1. Failure lives in the type: **`Option<T>`** = "a value or nothing" (Rust's `null` replacement); **`Result<T, E>`** = "a value, or an error."
2. **`match`** forces you to handle every case - the compiler won't let you forget one.
3. **`?`** is the everyday tool: unwrap on `Ok`/`Some`, return-early on `Err`/`None`. It keeps the happy path readable and only works in a function whose return type can carry the error.
4. **`panic!`** is for unrecoverable bugs ("this can't happen"); **`Result`** is for expected, recoverable failures the caller can handle.
5. **`.unwrap()`** panics on failure - fine in tests and proven-impossible cases, a crash-in-waiting in real code. Prefer `?`, `match`, or at least `.expect("why")`.
6. Read files with **`std::fs::read_to_string`**; let `main` return a `Result` so you can use `?` end to end.


---

# The Ecosystem & Tooling - Cargo Does Everything

If you've come from a language where the toolchain is a pile of separate things you bolt together - a build tool here, a package manager there, a formatter you had to be talked into, a test runner with its own config file - Rust's tooling story feels like a vacation. There's basically one tool, **Cargo**, and it does almost everything. It came with your Rust install in [Phase 1](01-install-and-first-program.md), and you'll spend your whole Rust life in it.

The point of this phase isn't to memorize commands - it's to give you the mental model, *what each tool is for and why it exists*, so the commands make sense and you reach for the right one without thinking.

📝 **Terminology.** A **crate** is Rust's word for a package - a library or program. **crates.io** is the public registry where the community publishes crates (like npm for JavaScript or PyPI for Python). A **dependency** is a crate your project uses. *(The backend of this very project - The Missing Manual's search engine - is a Rust crate built and tested with exactly these commands.)*

## Cargo: your one tool

**What it actually is.** Cargo is Rust's build tool *and* package manager *and* test runner, rolled into one. Where other ecosystems make you assemble that toolchain yourself, Rust ships it as a single, opinionated program: every Rust project on earth is built and tested the same way, so you can drop into any repo already knowing the commands.

Here are the four you'll use constantly, each the same `cargo <verb>` shape.

**`cargo build`** - compile your project.
```console
$ cargo build
   Compiling myapp v0.1.0 (/home/you/myapp)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.42s
```
*What just happened:* Cargo compiled your code (and any dependencies not yet built) and dropped the binary in `target/debug/`. "unoptimized + debuginfo" means a fast-to-compile *debug* build, great for development, not shipping. When ready to ship, `cargo build --release` produces an optimized binary in `target/release/` that's much faster to run (and slower to compile). The runtime-overflow check from [Phase 9](09-idioms-and-gotchas.md) is on in debug and off in release, another reason the two profiles exist.

**`cargo run`** - build *and* run in one step.
```console
$ cargo run
   Compiling myapp v0.1.0 (/home/you/myapp)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.40s
     Running `target/debug/myapp`
Hello, world!
```
*What just happened:* `cargo run` is `cargo build` plus "now execute the binary." The command you'll hammer most while developing - edit, `cargo run`, repeat. If nothing changed since the last build, it skips straight to "Running" without recompiling.

**`cargo add`** - add a dependency from crates.io.
```console
$ cargo add serde
    Updating crates.io index
      Adding serde v1.0.219 to dependencies
```
*What just happened:* Cargo looked up `serde` (a popular serialization crate) on crates.io, picked a compatible version, and wrote it into `Cargo.toml`. The next `cargo build` downloads and compiles it. Before `cargo add` existed you edited `Cargo.toml` by hand and guessed the version string - now the tool does it correctly.

**`cargo test`** - run your tests (we'll come back to this below).

## `Cargo.toml` and `Cargo.lock`: the manifest and the receipt

**What `Cargo.toml` actually is.** Your project's manifest - a small, human-edited file declaring the project's name, version, and dependencies. After `cargo add serde`, it looks like this:

```text
[package]
name = "myapp"
version = "0.1.0"
edition = "2024"

[dependencies]
serde = "1.0.219"
```
*What just happened:* `[package]` is your project's identity. `[dependencies]` lists the crates you depend on and the version ranges you accept (`"1.0.219"` means "1.0.219 or any compatible 1.x"). This is the file to read and edit to understand or change what a project depends on.

⚠️ **`Cargo.lock` is different - don't hand-edit it.** Alongside `Cargo.toml`, Cargo maintains `Cargo.lock`: the *exact* versions of every dependency (and sub-dependency) actually resolved. Think of `Cargo.toml` as "what I asked for" and `Cargo.lock` as "what I got, precisely." It's auto-generated; commit it (for applications) so teammates and CI build the identical dependency set, but never edit it yourself.

## `rustfmt`: stop arguing about formatting

**What it actually is.** `rustfmt` is the official code formatter. Run `cargo fmt` and it rewrites your code into the standard Rust style - indentation, spacing, line breaks - instantly and consistently.

**Why it exists.** Formatting debates are a waste of a team's life. With one official formatter everyone runs, every codebase looks the same, diffs stay clean, and nobody reviews a PR complaining about brace placement. You stop *thinking* about formatting at all.

```console
$ cargo fmt
```
*What just happened:* It said nothing and changed your files in place - that's success. `cargo fmt` is silent when it works. (In CI, `cargo fmt --check` instead *reports* any unformatted file and exits non-zero, without changing anything - that's how teams enforce it.) Run it before every commit and formatting stops being your problem.

## `clippy`: the linter that teaches you Rust

**What it actually is.** Clippy is Rust's linter, genuinely one of the best in any language. Where the compiler tells you what's *wrong*, clippy tells you what's *not idiomatic*: code that works but that an experienced Rust developer would write differently. It's earned its reputation as a patient teacher, since each warning explains the better way and *why*.

```rust
fn main() {
    let name = "world".to_string();
    if name.len() > 0 {
        println!("Hello, {}", name);
    }
}
```
```console
$ cargo clippy
    Checking myapp v0.1.0 (/home/you/myapp)
warning: length comparison to zero
 --> src/main.rs:3:8
  |
3 |     if name.len() > 0 {
  |        ^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!name.is_empty()`
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.95.0/index.html#len_zero
  = note: `#[warn(clippy::len_zero)]` on by default
```
*What just happened:* The code compiles fine - clippy isn't reporting an error. It's noticing `name.len() > 0` is a roundabout way of saying "not empty," and suggests the clearer `!name.is_empty()`, with a link to a fuller explanation. Follow clippy's advice for a few weeks and you'll absorb idiomatic Rust by osmosis, like a senior reviewer commenting on every line, for free.

💡 **Key point.** The everyday loop: `cargo fmt`, then `cargo clippy`, then `cargo test`, before you commit. Format, lint, verify. Many teams wire all three into CI so nothing un-formatted, un-lint-clean, or un-tested can merge.

## `cargo test`: tests live next to your code

**What it actually is.** Rust has testing built into the language and Cargo - no separate framework to install, no config file. Mark a function `#[test]`, put your tests in a module beside the code they test, and `cargo test` finds and runs them all.

```rust
fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    println!("{}", add(2, 2));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adds() {
        assert_eq!(add(2, 2), 4);
    }

    #[test]
    fn adds_negatives() {
        assert_eq!(add(-1, -1), -2);
    }
}
```
```console
$ cargo test
   Compiling myapp v0.1.0 (/home/you/myapp)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.13s
     Running unittests src/main.rs (target/debug/deps/myapp-8e8c850d1f800cd0)

running 2 tests
test tests::adds ... ok
test tests::adds_negatives ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
```
*What just happened:* Cargo compiled a special test build, found both `#[test]` functions, ran them, and reported each one. `assert_eq!(add(2, 2), 4)` checks the two values are equal and fails the test (with a clear diff) otherwise. `#[cfg(test)]` on the module means "only compile this when testing" - tests add zero weight to your shipped binary. Sitting tests right next to the code they cover is deliberate: writing one is so easy you actually do it.

> ⏭️ This is just enough to run tests. For *how to think about testing* - what to test, how much, and why - see [the Testing category](/guides/) guides.

## Recap

1. **Cargo is the one tool**: `cargo build` (compile), `cargo run` (build + run), `cargo add <crate>` (add a dependency), `cargo test` (run tests).
2. **`Cargo.toml`** is the manifest you edit (name, version, dependencies); **`Cargo.lock`** is the auto-generated exact-versions receipt you commit but don't touch.
3. **crates.io** is the public crate registry; `cargo add` pulls from it and updates `Cargo.toml` for you.
4. **`cargo fmt`** (rustfmt) formats your code to the one standard style - silent when it succeeds.
5. **`cargo clippy`** is the famously helpful linter: it teaches idiomatic Rust by explaining the better way, not just flagging the wrong way.
6. **`cargo test`** runs `#[test]` functions that live right beside your code - testing is built in, no framework required.


---

# Idioms & Common Gotchas - Writing Rust Like a Rustacean

There's a moment, a few weeks into Rust, when your code stops being "C with extra punctuation" and starts being *Rust* - you stop fighting the language and start leaning on it. This phase is a shortcut to that moment: the small set of patterns that make code feel native, plus a cheat-card of the gotchas that trip up everyone so they don't have to trip up you.

None of this is mandatory to write working programs, but these are the idioms you'll see in every real codebase (including [this project's Rust backend](08-ecosystem-and-tooling.md)) - idiomatic precisely because they make the compiler do more of the work for you.

## Enums + exhaustive `match`: model your data plainly

**What it actually is.** An enum is a type that is *exactly one of several variants*, and in Rust, each variant can carry its own data - far more powerful than the integer-constants "enum" in many languages. It lets you say "a shape is a circle *with a radius*, or a rectangle *with a width and height*," making any other possibility impossible.

```rust
#[derive(Debug)]
enum Shape {
    Circle(f64),
    Rectangle(f64, f64),
}

fn area(s: &Shape) -> f64 {
    match s {
        Shape::Circle(r) => std::f64::consts::PI * r * r,
        Shape::Rectangle(w, h) => w * h,
    }
}

fn main() {
    let shapes = [Shape::Circle(1.0), Shape::Rectangle(2.0, 3.0)];
    for s in &shapes {
        println!("{:?} area {:.2}", s, area(s));
    }
}
```
```console
$ cargo run
Circle(1.0) area 3.14
Rectangle(2.0, 3.0) area 6.00
```
*What just happened:* `match` looks at which variant `s` is and pulls its data out in one move - `Shape::Circle(r)` binds the radius to `r`. (`Option` and `Result` from [Phase 7](07-errors-and-io.md) are themselves just enums, why `match` works on them too.)

Now the superpower: add a variant - say `Triangle` - and forget to handle it, and the compiler stops you cold.

```console
$ cargo build
error[E0004]: non-exhaustive patterns: `&Shape::Triangle(_, _)` not covered
 --> src/main.rs:8:11
  |
8 |     match s {
  |           ^ pattern `&Shape::Triangle(_, _)` not covered
```
*What just happened:* `match` must be **exhaustive** - cover every variant. The compiler noticed `Triangle` had no arm and refused to build. One of Rust's quietest, most valuable features: add a case to your data model and the compiler hands you a to-do list of every place that needs updating. No silent "fell through the cracks" bug.

💡 **Key point.** Enums + exhaustive `match` mean "make illegal states unrepresentable, and impossible to forget to handle." Reach for an enum whenever a value is "one of a fixed set of things," especially when each thing carries different data.

## Traits: shared behavior without inheritance

**What it actually is.** A trait is a set of methods a type promises to provide - "anything that implements `Display` knows how to print itself nicely." It's how Rust does shared behavior in place of class inheritance: define a trait, implement it for any types you like, and functions can accept "anything that implements this trait."

```rust
trait Greet {
    fn greeting(&self) -> String;
}

struct Dog;
struct Robot;

impl Greet for Dog {
    fn greeting(&self) -> String {
        "Woof".to_string()
    }
}
impl Greet for Robot {
    fn greeting(&self) -> String {
        "BEEP BOOP".to_string()
    }
}

fn announce(thing: &impl Greet) {
    println!("{}", thing.greeting());
}

fn main() {
    announce(&Dog);
    announce(&Robot);
}
```
```console
$ cargo run
Woof
BEEP BOOP
```
*What just happened:* `Dog` and `Robot` share no common parent, but both implement `Greet`, so both can be passed to `announce(thing: &impl Greet)` - "give me a reference to anything that can `greeting`." How Rust gets polymorphism without inheritance hierarchies. You've already used traits without knowing it: `#[derive(Debug)]` implements `Debug`, which is what lets `{:?}` print your type.

## Iterator combinators: describe the transformation, skip the loop

**What they actually are.** Instead of writing an explicit `for` loop with a mutable accumulator, chain *combinators* - `.map()`, `.filter()`, `.collect()`, and friends - that describe *what* you want done to each element. The result reads top-to-bottom like a sentence, and it's just as fast as a hand-written loop (the compiler optimizes the chain away).

```rust
fn main() {
    let nums = vec![1, 2, 3, 4, 5, 6];

    let evens_squared: Vec<i32> = nums
        .iter()
        .filter(|&&n| n % 2 == 0)   // keep the even ones
        .map(|&n| n * n)            // square each
        .collect();                 // gather into a Vec

    println!("{:?}", evens_squared);
}
```
```console
$ cargo run
[4, 16, 36]
```
*What just happened:* Read it as a pipeline: start with the numbers, *filter* down to evens, *map* each to its square, then *collect* into a `Vec`. `.iter()` starts the chain (borrowing each element); `.collect()` ends it by building the final collection. The `&&n` looks odd at first - pattern-matching through two layers of reference - but you'll stop noticing it. No index variable, no off-by-one, no mutable accumulator to get wrong.

⚠️ **Iterators are lazy.** `.filter()` and `.map()` don't do anything by themselves - they just describe work. Nothing happens until a *consumer* like `.collect()`, `.sum()`, or a `for` loop pulls the values through. Forget the consuming step and your "transformation" silently does nothing.

## Option/Result combinators: handle the maybe without a `match`

The same combinator style works on `Option` and `Result`, letting you transform a maybe-value without a full `match` for the simple cases:

```rust
fn main() {
    let input = "42";

    // parse() gives Result; .ok() turns it into Option; map adjusts the value;
    // unwrap_or supplies a fallback if it was None.
    let doubled = input.parse::<i32>().ok().map(|n| n * 2).unwrap_or(0);

    println!("{}", doubled);
}
```
```console
$ cargo run
84
```
*What just happened:* `parse()` returned `Ok(42)`; `.ok()` converted that to `Some(42)`; `.map(|n| n * 2)` turned it into `Some(84)`; `.unwrap_or(0)` pulled out `84` (and would give `0` if anything had been `None`). That "parse, double if it worked, else default to zero" story is one readable line. For simple "transform or fall back" cases, combinators like `.map()`, `.unwrap_or()`, `.and_then()`, and `.unwrap_or_else()` beat a `match` - save `match` for genuinely different cases.

## Prefer borrowing over cloning

This is less a single trick and more a habit, the one that most separates comfortable Rust from "I `.clone()` everything to make the borrow checker stop." Remember from [Phase 6](06-ownership-and-borrowing.md): `.clone()` makes a full, independent copy. Sometimes you need that; often you reached for it just to dodge a move error, quietly adding a needless copy.

The idiomatic default: **functions should borrow (`&T`) what they only need to read, and take `&str` instead of `String`, `&[T]` instead of `Vec<T>`.** That way callers can pass what they already have without giving it up or copying it.

```rust
// Idiomatic: borrows, doesn't take ownership, accepts &str
fn shout(message: &str) -> String {
    message.to_uppercase()
}

fn main() {
    let owned = String::from("hello");
    println!("{}", shout(&owned));   // pass a borrow
    println!("{}", shout("world"));  // a literal &str works too
    println!("still have: {}", owned);  // owned wasn't moved or cloned
}
```
```console
$ cargo run
HELLO
WORLD
still have: hello
```
*What just happened:* `shout` takes `&str`, so it accepts a borrow of a `String` *and* a plain string literal, without either caller losing ownership or copying. `owned` is still usable afterward. Rule of thumb: borrow unless you have a real reason to own the value.

## The gotcha cheat-card

The things that bite every newcomer, with the calm fix for each. Skim it now, come back when one bites you.

| The gotcha | What's going on | The fix |
|---|---|---|
| **`String` vs `&str`** | `String` is an owned, growable string (heap). `&str` is a borrowed *view* into string data (a slice). Literals like `"hi"` are `&str`. | Take `&str` in function parameters (most flexible); use `String` when you need to own or grow it. Convert with `.to_string()` / `&s`. |
| **Clone overuse** | Sprinkling `.clone()` to silence the borrow checker quietly copies data everywhere. | Borrow (`&`) instead. Clone only when you truly need a second independent copy. |
| **"Fighting the borrow checker"** | An error feels unfair, so you push harder against it. | It's almost always flagging a real lifetime/aliasing issue. Restructure (borrow, narrow a scope, finish one borrow before the next) rather than forcing it. See [Phase 6](06-ownership-and-borrowing.md). |
| **Lifetime anxiety** (`'a`) | The `'a` syntax looks like deep wizardry. | It's just "how long this reference is valid." The compiler infers it 95% of the time; you only annotate when it asks. Don't let it scare you off. |
| **`.unwrap()` panics** | `.unwrap()` crashes the program on `Err`/`None`. Fine in tests, a landmine in real code. | Use `?` to bubble the error, or `match`/combinators to handle it. Reserve `.unwrap()`/`.expect()` for proven-impossible cases. See [Phase 7](07-errors-and-io.md). |
| **Integer overflow in debug** | In **debug** builds, `a + b` that overflows *panics*; in **release** builds it silently wraps around. So a bug can vanish in release. | Be intentional: use `.checked_add()` (returns `Option`), `.saturating_add()` (clamps), or `.wrapping_add()` (explicitly wraps) when overflow is possible. |

That last one surprises people, so let's see it. With a runtime value (so the check isn't caught at compile time), a debug build catches the overflow the moment it happens:

```rust
fn main() {
    let values: Vec<u8> = vec![255];
    let x = values[0];   // a u8, max value 255
    let y = x + 1;       // 256 doesn't fit in a u8
    println!("{}", y);
}
```
```console
$ cargo run
thread 'main' panicked at src/main.rs:4:13:
attempt to add with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
*What just happened:* `255 + 1` doesn't fit in a `u8` (tops out at 255), and the debug build's overflow check caught it and panicked with a precise message. This is a *feature*: in many languages this would silently wrap to `0` and corrupt your logic with no warning. ⚠️ The catch: a `--release` build turns this check off for speed and *does* silently wrap - so when overflow is genuinely possible, don't rely on the debug panic; reach for `.checked_add()` and handle the `None`.

## Recap

1. **Enums + exhaustive `match`**: model "one of a fixed set" plainly; the compiler forces you to handle every variant (`error[E0004]` when you don't).
2. **Traits**: shared behavior without inheritance - implement a trait for any type, then accept `&impl Trait`.
3. **Iterator combinators** (`.iter().filter().map().collect()`): describe the transformation instead of writing the loop - readable *and* fast. Remember they're lazy until consumed.
4. **`Option`/`Result` combinators** (`.map`, `.ok`, `.unwrap_or`, `.and_then`): handle the simple maybe-cases without a full `match`.
5. **Borrow over clone**: take `&T` / `&str` / `&[T]` to read; clone only when you truly need a second copy.
6. **The cheat-card** covers the six that bite everyone - `String` vs `&str`, clone overuse, borrow-checker fights, lifetime anxiety, `.unwrap()` panics, and debug-only integer-overflow panics.


---

# Lifetimes & the Borrow Checker, Deep - Making References Provable

Back in [Phase 6](06-ownership-and-borrowing.md) you learned to borrow: `&T` to read, `&mut T` to write, many readers or one writer. And in [Phase 9](09-idioms-and-gotchas.md) we admitted that the `'a` syntax gives people "lifetime anxiety" - it looks like deep wizardry. By the end of this phase, `'a` won't read as magic - it'll read as the compiler spelling out a relationship you already understand.

Here's the reframe to carry through: **a lifetime is not something you create. It's a region of code where a reference is valid.** The compiler tracks those regions for every reference, all the time - you just don't usually see it. Annotations like `'a` aren't *making* lifetimes; they're *naming* ones that already exist so the compiler can check a relationship it couldn't otherwise figure out.

## The problem lifetimes solve

Every borrowing rule in Phase 6 protects one promise: **a reference must never outlive the data it points to.** If it did, you'd have a *dangling reference* - a pointer to memory that's already been freed. Reading through it is undefined behavior: a crash, garbage data, or a security hole. Languages without this guarantee (C, C++) leak this bug constantly. Rust makes it *impossible*, at compile time.

Lifetimes are the bookkeeping that makes that possible. Watch the compiler catch the bug:

```rust
fn main() {
    let r;                      // r will hold a reference
    {
        let x = 5;              // x lives only inside this block
        r = &x;                 // borrow x
    }                           // x is dropped here - its memory is gone
    println!("{}", r);          // ...but r still points at it
}
```
```console
$ cargo build
error[E0597]: `x` does not live long enough
 --> src/main.rs:5:13
  |
4 |         let x = 5;
  |             - binding `x` declared here
5 |         r = &x;
  |             ^^ borrowed value does not live long enough
6 |     }
  |     - `x` dropped here while still borrowed
7 |     println!("{}", r);
  |                    - borrow later used here
```
*What just happened:* `x` was born and died inside the inner block. `r` borrowed it, then tried to outlive it. The compiler compared two regions - the region `x` is alive for, and the region `r` needs its borrow to be valid for - and saw `r`'s need extending *past* `x`'s death. That mismatch is the whole error. It even narrates the timeline: "declared here," "dropped here while still borrowed," "borrow later used here." No dangling reference ships.

📝 **Lifetime** - the region of code over which a reference is guaranteed valid (roughly, from where it's created to its last use, but never past the moment its referent is dropped). It's a property the compiler *infers and checks*, not a value you store or pass around.

💡 **Key point.** The borrow checker isn't doing anything new here - it's the same "references can't outlive their data" rule from Phase 6, now visible. Lifetimes name *how long* each reference is allowed to be valid. Most of the time the compiler works this out silently; you only get involved when it genuinely can't.

## Why a function sometimes needs annotations

Inside one function, the compiler can see every variable's scope, so it figures out lifetimes on its own. The trouble starts at **function boundaries**. When a function returns a reference, the compiler - looking only at the signature - has to know *which input that reference borrows from*, or it can't tell the caller how long the returned reference stays valid.

The classic case is a function that returns the longer of two string slices:

```rust
fn longest(a: &str, b: &str) -> &str {
    if a.len() > b.len() { a } else { b }
}

fn main() {
    println!("{}", longest("hello", "hi"));
}
```
```console
$ cargo build
error[E0106]: missing lifetime specifier
 --> src/main.rs:1:33
  |
1 | fn longest(a: &str, b: &str) -> &str {
  |               ----     ----     ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the
          signature does not say whether it is borrowed from `a` or `b`
help: consider introducing a named lifetime parameter
  |
1 | fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
  |           ++++     ++          ++          ++
```
*What just happened:* The compiler hit a fork. The returned `&str` might come from `a` *or* `b` - the `if` decides at runtime. So how long is the result valid? It depends on which input it came from, and the signature doesn't say. The error is precise: "whether it is borrowed from `a` or `b`." This is the *one* situation where the compiler needs you to spell out the relationship.

The fix is exactly what the help text shows - add a lifetime parameter:

```rust
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.len() > b.len() { a } else { b }
}

fn main() {
    let s1 = String::from("hello");
    let s2 = String::from("hi");
    println!("longest is {}", longest(&s1, &s2));
}
```
```console
$ cargo run
longest is hello
```
*What just happened:* `<'a>` declares a lifetime *name* - read it as "for some region `'a`." Then `a: &'a str` and `b: &'a str` say "both inputs are borrowed for at least `'a`," and `-> &'a str` says "the returned reference is valid for `'a` too." You haven't changed *what the code does* - `longest` still returns the longer slice. You've added a *promise to the compiler*: the result lives no longer than the shorter-lived of the two inputs, so the caller knows exactly how long it can hold the result.

📝 **`<'a>`** - a *lifetime parameter*. The leading apostrophe marks it as a lifetime (not a type), and `a` is just a name - `'a` is conventional, like `i` for a loop counter. `&'a str` means "a `&str` valid for the region named `'a`." You're not setting the lifetime; you're giving the compiler a label so it can connect inputs to outputs and verify the whole thing holds.

⚠️ **Annotations describe; they don't extend.** A common misread is thinking `'a` *makes* a reference live longer. It can't - lifetimes only *document* relationships the compiler then checks. If you tell it the output lives as long as `a`, but a caller drops `a` too early, you get an error at the *call site* - the annotation is what makes that check possible, not a way to dodge it.

## Lifetime elision - why you almost never write `'a`

If returning a reference needs lifetimes, why have you written dozens of functions taking `&self` or `&str` without ever typing `'a`? Because the compiler applies **lifetime elision rules** - a short list of obvious patterns where it fills in the lifetimes for you. When your function fits a pattern, you write nothing. `longest` failed only because it fit *none* of them (two input references, ambiguous source).

The three rules the compiler applies, in order:

1. **Each input reference gets its own lifetime.** `fn f(x: &str, y: &str)` is treated as `fn f<'a, 'b>(x: &'a str, y: &'b str)`.
2. **If there's exactly one input lifetime, it's assigned to all outputs.** One reference in, references out - they all borrow from that input. This covers the vast majority of functions.
3. **If one of the inputs is `&self` or `&mut self`, `self`'s lifetime goes to all outputs.** This is why struct methods almost never need annotations.

Here's a function that *looks* like it needs a lifetime but doesn't, thanks to rule 2:

```rust
fn first_word(s: &str) -> &str {
    s.split(' ').next().unwrap_or("")
}

fn main() {
    let sentence = String::from("hello world");
    println!("{}", first_word(&sentence));
}
```
```console
$ cargo run
hello
```
*What just happened:* `first_word` returns a reference, yet compiles with no `'a` in sight. Rule 2 did the work: there's exactly one input reference (`s`), so the compiler assigns its lifetime to the return type automatically. Behind the scenes the signature is `fn first_word<'a>(s: &'a str) -> &'a str` - you did not have to type it. `longest` couldn't use this rule because it had *two* input references and no `self`, leaving the source genuinely ambiguous.

💡 **You've been using lifetimes all along.** Every `&str` parameter, every method returning `&self.field`, every borrow you've written since Phase 6 had lifetimes - the compiler just inferred them via elision. Annotations are the same thing made explicit for the handful of cases where inference can't guess.

## References in structs

So far our references have lived in local variables and function signatures. You can also store a reference *inside a struct* - but the moment you do, the struct picks up a lifetime parameter. The reason follows directly from the core rule: if a struct holds a reference, **the struct must not outlive the data it points to.** A lifetime parameter is how the struct carries that constraint.

```rust
struct Excerpt<'a> {
    text: &'a str,          // a borrowed slice, not an owned String
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().unwrap();
    let e = Excerpt { text: first_sentence };
    println!("Excerpt: {}", e.text);
}
```
```console
$ cargo run
Excerpt: Call me Ishmael
```
*What just happened:* `Excerpt<'a>` declares that the struct holds a reference valid for region `'a`, and `text: &'a str` ties the field to it. Read it aloud: "an `Excerpt` cannot outlive the `&str` it borrows." Here `novel` owns the string and lives for the whole `main`, while `e` borrows a slice of it and is dropped first - so the constraint holds and it compiles. Drop `novel` while the `Excerpt` was still around and you'd get the same `does not live long enough` error from the first section. The lifetime parameter is what lets the compiler enforce that.

```mermaid
flowchart LR
  N["novel: String<br/>(owns the data)"] -->|borrowed by| E["Excerpt&lt;'a&gt;<br/>text: &'a str"]
  E -.->|must NOT outlive| N
```

That diagram is the whole rule in one picture: the `Excerpt` borrows from `novel`, so it must be gone before `novel` is. The `'a` on the struct is what makes the compiler check the dotted arrow.

⚠️ **A struct holding a reference is a deliberate choice.** It ties the struct's lifespan to the data it borrows, which can ripple constraints through your code. Often the simpler design is to store an *owned* `String` instead of a `&'a str` - the struct then owns its data and has no lifetime parameter at all. Reach for borrowed fields when you specifically want to avoid copying and can guarantee the owner outlives the struct.

## `'static` - the lifetime of the whole program

There's one lifetime name with special meaning: **`'static`**. A reference with lifetime `'static` is valid for the *entire duration of the program*. The most common holders are **string literals**:

```rust
fn main() {
    let s: &'static str = "I live for the whole program";
    println!("{}", s);
}
```
```console
$ cargo run
I live for the whole program
```
*What just happened:* The literal `"I live for the whole program"` is baked into the program's binary, so it exists from start to finish - its lifetime really is `'static`. Every string literal you've ever written is a `&'static str`; you just never had to name it. Annotating it here is optional, done only to show what's going on.

⚠️ **`'static` does not mean "leak it."** The widespread misconception is that adding `'static` forces data to live forever by keeping it allocated (a memory leak). That's backwards. `'static` is a *claim* that data already lives for the whole program - exactly true of literals and constants, no leaking involved. Slapping `'static` on a reference to short-lived data doesn't extend it; the compiler just rejects the claim with a `does not live long enough` error. Use it to describe genuinely program-long data, not as a hammer to silence lifetime errors.

## Recap

1. **The problem lifetimes solve:** a reference must never outlive the data it points to. Lifetimes are how the compiler *proves* this and makes dangling references impossible (`error[E0597]: does not live long enough`).
2. **A lifetime is a region, not a thing you create.** Annotations like `'a` *name* an existing region so the compiler can check a relationship - they describe, they never extend.
3. **Functions need annotations when a returned reference is ambiguous** about which input it borrows from (`longest`). `<'a>` ties inputs and outputs together so the caller knows how long the result is valid.
4. **Lifetime elision** fills in lifetimes automatically for common patterns (one input reference, or a `&self` method), which is why you rarely type `'a` - you've been using lifetimes all along.
5. **A struct holding a reference needs a lifetime parameter** (`Excerpt<'a>`), encoding the rule that the struct must not outlive the data it borrows. Owning the data instead avoids the parameter entirely.
6. **`'static`** is the lifetime of data that lives the whole program (string literals). It means "already valid for the whole program," *not* "leak it to make it live forever."

You can now read a lifetime annotation and see the relationship it describes, instead of bracing for a fight. Next: **traits and generics** - how Rust writes code once and runs it on many types, with the compiler still checking everything.

## Quick check

Test yourself on the idea that demystifies this phase - that a lifetime *describes* a region rather than creating one:

```quiz
[
  {
    "q": "What does a lifetime annotation like `'a` actually do?",
    "choices": [
      "Names a region of code so the compiler can check that a reference doesn't outlive its data",
      "Forces the referenced data to stay allocated for longer",
      "Creates a new lifetime that extends how long a value lives",
      "Tells the garbage collector when to free the value"
    ],
    "answer": 0,
    "explain": "A lifetime is a region the compiler already tracks. Annotations only name that region so it can verify a relationship (like 'this output borrows from this input'). They describe and check - they never extend a value's life, and Rust has no garbage collector."
  },
  {
    "q": "Why does `fn longest(a: &str, b: &str) -> &str` need a lifetime parameter, while `fn first_word(s: &str) -> &str` does not?",
    "choices": [
      "`longest` has two input references and could return either, so the source of the output is ambiguous; `first_word` has one input, so elision assigns its lifetime to the output",
      "`longest` is more complex, so the compiler gives up and asks for help",
      "`first_word` returns an owned String, which never needs a lifetime",
      "Functions with an `if` always need lifetime parameters"
    ],
    "answer": 0,
    "explain": "Lifetime elision rule 2 covers exactly one input reference: its lifetime flows to the output, so `first_word` needs no annotation. `longest` has two input references and the returned slice could come from either, so the compiler can't infer which one - you must spell it out."
  },
  {
    "q": "Your friend says: \"Add `'static` to this reference so the data lives forever and the lifetime error goes away.\" What's wrong with that?",
    "choices": [
      "`'static` claims the data already lives for the whole program; it can't extend short-lived data, so the compiler just rejects the false claim",
      "Nothing - `'static` is the correct way to silence any lifetime error",
      "`'static` works but causes a guaranteed memory leak every time",
      "`'static` only works on numbers, not on references"
    ],
    "answer": 0,
    "explain": "`'static` is a claim that data is valid for the entire program (true for literals and constants), not a command to keep data alive. Applied to short-lived data, the claim is false and the compiler rejects it with a 'does not live long enough' error. It's a description, not a fix-all."
  }
]
```


---

# Traits & Generics, Deep - Shared Behavior Without Inheritance

Back in [Phase 9](09-idioms-and-gotchas.md) you met traits as a one-paragraph idea: a set of methods a type promises to provide, and `&impl Trait` to accept "anything that has them." That was the postcard. This phase is the country.

Here's the mental model to carry through: most object-oriented languages share behavior by *inheritance* - a `Dog` **is a** `Animal` and gets `Animal`'s methods by being born into the family tree. Rust has no inheritance at all. Instead it shares behavior by *capability*: a type **can do** a thing if it implements the trait for that thing. A `Dog` isn't an `Animal`; it's something that *can* `Speak`. Once that flip clicks - from "what is it" to "what can it do" - traits and generics stop feeling like two separate features and become the single answer to "how does Rust reuse code without classes."

## Traits as shared behavior - now with defaults

Quick recap: a **trait** is a named set of method signatures. You write `impl ThatTrait for YourType` to fulfill the promise, and afterward your type can be used anywhere the trait is required.

📝 **Trait** - a contract describing behavior: a list of methods a type must provide to "implement" it. It's the unit of shared behavior in Rust, the rough equivalent of an interface in other languages - but with one extra power: a trait can ship *default* implementations.

That extra power is the new part. A trait method doesn't have to be just a signature - it can supply a working body. Implementors get that body for free and only override it when they want something different.

```rust
trait Greet {
    fn name(&self) -> String;

    // Default method: built from the required one above.
    fn greeting(&self) -> String {
        format!("Hello, I'm {}", self.name())
    }
}

struct Dog;
struct Robot;

impl Greet for Dog {
    fn name(&self) -> String {
        "Rex".to_string()
    }
    // No greeting() here - Dog takes the default.
}

impl Greet for Robot {
    fn name(&self) -> String {
        "Unit-7".to_string()
    }
    // Robot overrides the default with its own version.
    fn greeting(&self) -> String {
        "BEEP BOOP. DESIGNATION: UNIT-7".to_string()
    }
}

fn main() {
    println!("{}", Dog.greeting());
    println!("{}", Robot.greeting());
}
```
```console
$ cargo run
Hello, I'm Rex
BEEP BOOP. DESIGNATION: UNIT-7
```
*What just happened:* `Dog` only implemented `name`, so it inherited the default `greeting`. `Robot` implemented both, so its custom `greeting` won. This is how the standard library keeps big traits ergonomic: implement one or two core methods and get dozens of derived ones free - `Iterator` is the famous example, where you write `next` and the trait hands you `map`, `filter`, `sum`, and the rest as defaults.

💡 **Key point.** Default methods let a trait carry *behavior*, not just a *shape*. Required methods are the small surface you must implement; default methods are the convenience layer on top. Design your traits with a minimal required core and rich defaults.

## Generics with trait bounds - write it once, for every type

Traits answer "what can a type do." Generics answer "how do I write one function that works for *many* types." A generic function uses a **trait bound** to say "this works for any type `T`, *as long as* `T` can do X."

The classic example is "find the largest item." Without generics you'd write one `largest` for `i32`, another for `f64`, another for `char` - identical logic, copy-pasted. With a generic, write it once:

```rust
// T can be any type, AS LONG AS values of T can be compared with > and <.
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut biggest = &list[0];
    for item in list {
        if item > biggest {   // requires T: PartialOrd
            biggest = item;
        }
    }
    biggest
}

fn main() {
    let numbers = vec![34, 50, 25, 100, 65];
    let chars = vec!['y', 'm', 'a', 'q'];
    println!("largest number: {}", largest(&numbers));
    println!("largest char:   {}", largest(&chars));
}
```
```console
$ cargo run
largest number: 100
largest char:   y
```
*What just happened:* `<T: PartialOrd>` reads "for any type `T` that implements `PartialOrd`," the trait providing `<`, `>`, `<=`, `>=`, so the bound *unlocks* the `item > biggest` comparison inside the body. Drop the bound and the compiler rejects the function, since `T` might otherwise be a type that can't be compared. The bound is a promise the compiler holds you to *and* lets you rely on.

When bounds pile up, the inline `<T: A + B, U: C>` form gets noisy. The `where` clause moves them out of the signature line for readability - purely cosmetic, same meaning:

```rust
use std::fmt::Display;

// Harder to read:
fn show_both<T: Display + Clone, U: Display + Clone>(a: T, b: U) -> String {
    format!("{} and {}", a, b)
}

// Same thing, easier to read:
fn show_both_clean<T, U>(a: T, b: U) -> String
where
    T: Display + Clone,
    U: Display + Clone,
{
    format!("{} and {}", a, b)
}

fn main() {
    println!("{}", show_both(1, "two"));
    println!("{}", show_both_clean(3.5, 'x'));
}
```
```console
$ cargo run
1 and two
3.5 and x
```
*What just happened:* both functions are identical to the compiler; `where` is the readable spelling once you have more than one or two bounds.

Now the part that makes generics *free* at runtime. When you call `largest(&numbers)` and `largest(&chars)`, the compiler doesn't keep one mysterious "generic" function around and figure out types at runtime. It stamps out a concrete copy for each type you actually used - `largest_i32`, `largest_char` - and compiles each as if you'd written it by hand.

📝 **Monomorphization** - the compile-time process where Rust replaces a generic function (or type) with specialized concrete copies, one per set of type arguments actually used in your program. "Mono" = one, "morph" = form: each generic becomes several single-form versions.

💡 **Key point.** Monomorphization is why Rust generics cost *nothing* at runtime - no boxing, no type tag, no lookup. The generated machine code is the same as if you'd hand-written a version per type. The price is paid at compile time and in binary size, not in speed. This "zero-cost abstraction" is a recurring Rust theme: the convenience is real, and the runtime bill is zero.

## Associated types - when a trait has one logical companion type

Sometimes a trait needs to refer to *another* type that depends on the implementor. The iterator is the textbook case: an iterator produces items, but the *kind* of item differs per iterator - a number range yields `i32`, a lines reader yields `String`. The trait needs a slot for "the type I produce."

You *could* make that a generic parameter: `trait Iterator<Item>`. But there's a sharper tool. When there is exactly **one** logical choice of companion type per implementing type, use an **associated type** instead - a type slot the *implementor* fills in, not the caller.

```rust
struct Counter {
    count: u32,
}

impl Iterator for Counter {
    type Item = u32;   // THIS counter always yields u32 - one logical choice.

    fn next(&mut self) -> Option<Self::Item> {
        if self.count < 3 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

fn main() {
    let counter = Counter { count: 0 };
    let collected: Vec<u32> = counter.collect();  // map/collect come free from Iterator's defaults
    println!("{:?}", collected);
}
```
```console
$ cargo run
[1, 2, 3]
```
*What just happened:* `Iterator` declares `type Item;` as an associated type; `type Item = u32` fixes it for `Counter` specifically, and `next` returns `Option<Self::Item>`, i.e. `Option<u32>`. Because we satisfied `Iterator`'s one required method (`next`), we got `collect`, `map`, `filter`, and the rest as default methods - and we never write `Counter`'s item type at the call site, since it's baked into the impl.

⚠️ **Gotcha - associated type vs. generic parameter.** The difference is *who chooses*. With an associated type (`type Item`), the implementor picks once and there's a single `impl Iterator for Counter`. With a generic parameter (`impl Iterator<u32> for Counter`), the *caller* could ask for different item types, and you'd need `impl Iterator<u32> for Counter`, `impl Iterator<String> for Counter`, and so on. Use an associated type when "one type per impl" is the truth - it keeps signatures clean (`Counter::next` not `Counter::<u32>::next`) and stops nonsensical multiple impls. Reach for a generic parameter only when multiple companion types per implementor genuinely make sense.

## Static vs dynamic dispatch - fast-and-fixed vs flexible-and-runtime

Everything so far - `&impl Trait`, generics with bounds - resolves at **compile time**. The compiler knows the concrete type at the call site and monomorphizes a specialized version. This is **static dispatch**: fast, inlinable, zero runtime overhead. But it has a hard limit: every value in a generic context must be the *same* concrete type. You cannot put a `Dog` and a `Robot` in the same `Vec<T>`.

When you genuinely need a *mixed* collection - shapes of different kinds in one list, plugins of different types behind one interface - you need **dynamic dispatch**: `dyn Trait`, a "trait object." Here the concrete type is *erased* and the right method is found at runtime through a lookup table.

📝 **Static dispatch** - the compiler resolves which concrete method to call at compile time (via generics / `impl Trait` / monomorphization). No runtime cost; fully inlinable. **Dynamic dispatch** - the concrete type is unknown at compile time; at runtime the program follows a pointer in a **vtable** (virtual method table) to find the right method body - one shared function handles all types, at the cost of an indirection.

Here's both, side by side. First static dispatch with a generic - fast, but each call site is locked to one type:

```rust
trait Shape {
    fn area(&self) -> f64;
}

struct Circle { r: f64 }
struct Square { side: f64 }

impl Shape for Circle {
    fn area(&self) -> f64 { std::f64::consts::PI * self.r * self.r }
}
impl Shape for Square {
    fn area(&self) -> f64 { self.side * self.side }
}

// STATIC dispatch: monomorphized per concrete type, inlined, zero overhead.
fn print_area(shape: &impl Shape) {
    println!("area = {:.2}", shape.area());
}

fn main() {
    print_area(&Circle { r: 1.0 });
    print_area(&Square { side: 2.0 });
}
```
```console
$ cargo run
area = 3.14
area = 4.00
```
*What just happened:* `&impl Shape` is sugar for a generic bound. The compiler generated one `print_area` specialized for `Circle` and one for `Square`, each with the `area` call inlined directly - no runtime "which type is this?" step, the answer was settled at compile time. Fast, but `print_area` can never hold a `Circle` and a `Square` at the same time.

Now dynamic dispatch, which makes a *heterogeneous* collection possible:

```rust
trait Shape {
    fn area(&self) -> f64;
}

struct Circle { r: f64 }
struct Square { side: f64 }

impl Shape for Circle {
    fn area(&self) -> f64 { std::f64::consts::PI * self.r * self.r }
}
impl Shape for Square {
    fn area(&self) -> f64 { self.side * self.side }
}

fn main() {
    // A Vec holding DIFFERENT concrete types behind one trait object.
    let shapes: Vec<Box<dyn Shape>> = vec![
        Box::new(Circle { r: 1.0 }),
        Box::new(Square { side: 2.0 }),
    ];

    let mut total = 0.0;
    for shape in &shapes {
        total += shape.area();   // runtime vtable lookup picks Circle::area or Square::area
    }
    println!("total area = {:.2}", total);
}
```
```console
$ cargo run
total area = 7.14
```
*What just happened:* `Box<dyn Shape>` is a trait object - a value whose concrete type is erased behind the `Shape` interface. The `Box` is needed because different shapes have different sizes and can't live inline in the `Vec`; it puts each on the heap so the `Vec` holds same-sized pointers. Each `shape.area()` doesn't know at compile time whether it's a circle or a square - at runtime it follows the value's vtable pointer to the correct implementation. *That* indirection buys you a single `Vec` of mixed types, which static dispatch flatly cannot do.

A trait object is really two pointers: one to the data, one to the vtable. The method call is "follow the vtable pointer, find `area`, jump there":

```mermaid
flowchart LR
  A["Box&lt;dyn Shape&gt;"] --> B[data pointer]
  A --> C[vtable pointer]
  C --> D["vtable: Circle"]
  D --> E["area -> Circle::area"]
```

⚠️ **Gotcha - the trade-off is real, pick deliberately.** Static dispatch is faster (inlinable, no indirection) but bloats binary size with one copy per type and *cannot* hold mixed types. Dynamic dispatch keeps code small and enables heterogeneous collections and runtime-chosen behavior, but pays a vtable indirection per call and blocks inlining. Rule of thumb: **default to generics/`impl Trait`; reach for `dyn Trait` when you specifically need a mixed collection, a runtime-selected implementation, or to keep generic code from exploding the binary.** In practice the per-call cost of `dyn` is tiny - don't contort your design to avoid it when it's the natural fit.

## Blanket impls & the orphan rule

Two final pieces show how far traits + generics reach.

A **blanket impl** is implementing a trait for *every* type that satisfies some bound - a generic impl. The standard library does this constantly. The most famous: any type that implements `Display` automatically gets `ToString`:

```rust
// This is (essentially) in the standard library:
//   impl<T: Display> ToString for T { ... }
//
// So you never implement ToString yourself. Implement Display, get ToString free.
use std::fmt;

struct Celsius(f64);

impl fmt::Display for Celsius {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}°C", self.0)
    }
}

fn main() {
    let temp = Celsius(21.5);
    let s: String = temp.to_string();   // .to_string() came from the blanket impl
    println!("{}", s);
}
```
```console
$ cargo run
21.5°C
```
*What just happened:* we never wrote `impl ToString for Celsius`. Because the standard library has `impl<T: Display> ToString for T`, implementing `Display` for `Celsius` *automatically* gave it `to_string()`. One blanket impl gives a method to thousands of types at once - that's the power of "implement a trait for all `T` matching a bound."

That power comes with a guardrail. You can implement a trait for a type only if **you own the trait, or you own the type** (at least one of the two must be defined in your crate). This is the **orphan rule**.

📝 **Orphan rule** - you may write `impl Trait for Type` only if `Trait` or `Type` (or both) is local to your crate. You cannot implement a *foreign* trait for a *foreign* type - e.g. you can't write `impl Display for Vec<i32>`, because both belong to the standard library, not you.

Why does it exist? Coherence. If crate A could `impl Display for Vec<i32>` and crate B could *also* implement it differently, and your program used both, which one wins? There'd be no answer - two conflicting truths about the same type. The orphan rule makes that impossible: every `(trait, type)` pair has at most one implementation across the ecosystem. When you *do* need to implement a foreign trait on a foreign type, the workaround is the **newtype pattern**: wrap it in a one-field tuple struct you own (`struct MyVec(Vec<i32>)`), and now the type is local, so the impl is allowed.

💡 **Key point.** Step back and see the whole picture: traits define capabilities, generics + bounds write code once against those capabilities, monomorphization makes it free, trait objects make it flexible when you need mixed types, and blanket impls + the orphan rule let the whole ecosystem compose without collisions. *This* is Rust's answer to polymorphism - no base classes, no inheritance trees, just "what can this type do."

## Recap

1. A **trait** is a contract of behavior. Beyond method signatures, it can ship **default methods** built on the required ones - implement a small core, inherit the convenience layer (the way `Iterator` gives you `map`/`filter`/`collect` from just `next`).
2. **Generics with trait bounds** (`fn f<T: Bound>` or a `where` clause) write one function for every type that satisfies the bound. The bound both restricts `T` and unlocks the trait's methods inside the body.
3. **Monomorphization** stamps out a specialized concrete copy per type used, so generics are zero-cost at runtime - the price is compile time and binary size, never speed.
4. An **associated type** (`type Item;`) is a type slot the *implementor* fills when there's exactly one logical choice per impl; it keeps signatures clean versus a caller-chosen generic parameter.
5. **Static dispatch** (generics / `impl Trait`) resolves at compile time - fast, inlinable, but single-type. **Dynamic dispatch** (`dyn Trait`, a trait object) uses a runtime **vtable** lookup - needed for heterogeneous collections like `Vec<Box<dyn Shape>>`, at the cost of an indirection.
6. A **blanket impl** implements a trait for all `T` matching a bound (`impl<T: Display> ToString for T`); the **orphan rule** (own the trait or the type) keeps implementations unambiguous across crates, with the newtype pattern as the escape hatch.

## Quick check

Test yourself on the distinctions that matter most:

```quiz
[
  {
    "q": "Why does `fn largest<T: PartialOrd>(list: &[T])` need the `: PartialOrd` bound?",
    "choices": [
      "Without it, the compiler can't guarantee values of `T` support `>` / `<`, so the comparison in the body wouldn't be allowed",
      "It makes the function run faster by skipping runtime type checks",
      "It tells the compiler to allocate the list on the heap instead of the stack",
      "It's optional styling - the function compiles fine without any bound"
    ],
    "answer": 0,
    "explain": "A trait bound both restricts which types `T` may be and unlocks that trait's methods inside the body. `PartialOrd` provides `<`/`>`; without the bound, `T` could be a non-comparable type, so `item > biggest` would be rejected."
  },
  {
    "q": "You need a single `Vec` that holds several different shape types together and calls `.area()` on each. Which tool fits?",
    "choices": [
      "`Vec<Box<dyn Shape>>` - dynamic dispatch, because a generic `Vec<T>` can only hold one concrete type",
      "`Vec<impl Shape>` - static dispatch handles mixed types automatically",
      "A separate generic `Vec<T>` per shape type, merged at runtime",
      "It's impossible in Rust; mixed-type collections aren't supported at all"
    ],
    "answer": 0,
    "explain": "Generics/`impl Trait` are static dispatch: every element must be the same concrete type. A heterogeneous collection needs trait objects - `Vec<Box<dyn Shape>>` - where the concrete type is erased and `area` is resolved at runtime via the vtable."
  },
  {
    "q": "Which `impl` does the orphan rule FORBID?",
    "choices": [
      "`impl Display for Vec<i32>` - both `Display` and `Vec` are foreign (from std), so you own neither",
      "`impl Display for MyStruct` - you own `MyStruct`",
      "`impl MyTrait for Vec<i32>` - you own `MyTrait`",
      "`impl MyTrait for MyStruct` - you own both"
    ],
    "answer": 0,
    "explain": "The orphan rule allows `impl Trait for Type` only if you own the trait OR the type. `Display` and `Vec` are both from the standard library, so that impl is forbidden - wrap `Vec` in a newtype you own to work around it."
  }
]
```


---

# Smart Pointers & Interior Mutability - Box, Rc, RefCell & Friends

Back in [Phase 6](06-ownership-and-borrowing.md) you learned Rust's iron law: every value has exactly one owner, and borrowing is checked at compile time - many readers *or* one writer, never both. That's what makes Rust safe with no garbage collector. But sooner or later you hit a wall it can't get you over: a linked list where each node needs to *be owned somewhere* but the type is defined in terms of itself; a value two parts of your program genuinely need to *share*; a tree where a child needs to update its parent. Taken literally, single ownership says these are impossible.

They're not. The escape hatches are **smart pointers**, and they don't *break* the ownership rules - they *bend them in controlled, documented ways*. Each one says exactly which rule it relaxes and what you pay for the privilege. Learn the four you'll actually use (`Box`, `Rc`, `Arc`, `RefCell`) and the two traits underneath them (`Deref`, `Drop`), and these stop being scary library types and become a toolkit you reach into deliberately.

📝 **Smart pointer** - a struct that *acts like* a pointer (dereference with `*`, call methods through it) but carries extra behavior: heap allocation, a reference count, runtime-checked borrowing, custom cleanup. `String` and `Vec<T>` are smart pointers too - they own heap data and clean it up for you - you've been using them all along.

## `Box<T>` - put one value on the heap

**What it actually is.** `Box<T>` is the simplest smart pointer: it takes a value, stores it on the **heap**, and gives you a fixed-size handle to it on the stack. Ownership works exactly as before - one owner, freed when the `Box` drops. The only thing that changed is *where the data lives*.

Most of the time you don't need a `Box` - Rust happily puts values on the stack. You reach for one when a value is too large to copy around on the stack, and - more importantly - when a type would otherwise have *infinite size*.

**Why recursive types need it.** Imagine a "cons list" (a list built from nested pairs, the classic Lisp shape). Written naively, each node contains the next node directly:

```rust
enum List {
    Cons(i32, List),   // a List that contains a List that contains a List...
    Nil,
}
```
```console
$ cargo build
error[E0072]: recursive type `List` has infinite size
 --> src/main.rs:1:1
  |
1 | enum List {
  | ^^^^^^^^^
2 |     Cons(i32, List),
  |               ---- recursive without indirection
  |
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
  |
2 |     Cons(i32, Box<List>),
  |               ++++    +
```
*What just happened:* To lay out `List`, the compiler needs its size. But `Cons` contains a `List`, which contains a `List`, which contains... forever. The fix it suggests: put the inner `List` behind a `Box`. A `Box` is a pointer - always the same small, known size - so the recursion stops. Take the hint:

```rust
#[derive(Debug)]
enum List {
    Cons(i32, Box<List>),
    Nil,
}

use List::{Cons, Nil};

fn main() {
    // 1 -> 2 -> 3 -> Nil
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
    println!("{:?}", list);
}
```
```console
$ cargo run
Cons(1, Cons(2, Cons(3, Nil)))
```
*What just happened:* Each node now stores an `i32` and a `Box<List>` - a value plus a pointer to the next node on the heap. The type has a finite size, so it compiles, and the list can be as long as you like. `Box::new(...)` moves a value to the heap; the `Box` owns it and frees it when dropped - the everyday use of `Box`, especially for recursive shapes like lists and tree nodes.

💡 **Key point.** `Box<T>` changes *where* a value lives, not *who* owns it or *how* it's borrowed - one owner, normal borrow rules. It's the smart pointer that bends the fewest rules, which is exactly why it's the one to reach for first.

## `Rc<T>` - many owners, single-threaded

Now we bend a real rule. Sometimes a value genuinely needs **more than one owner**: two nodes pointing at the same shared child, several parts of a structure that all need to keep a configuration alive. Plain ownership forces you to pick one owner and hand everyone else borrows, then fight lifetimes to prove those borrows don't outlive it. `Rc<T>` sidesteps that.

📝 **`Rc<T>`** ("reference counted") - a smart pointer that allows **multiple owners** of the same value by keeping a count of how many owners exist. Each `Rc::clone` bumps the count up; each drop bumps it down; the value frees at zero. No single owner has to outlive the others - the data lives exactly as long as *someone* still holds an `Rc` to it.

The crucial detail: `Rc::clone` does **not** deep-copy the data. It copies the pointer and increments the count - cheap, no matter how big the underlying value. (By convention you write `Rc::clone(&a)` rather than `a.clone()`, to signal "this is a cheap refcount bump, not a deep copy.")

```rust
use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("shared data"));
    println!("count after creating a: {}", Rc::strong_count(&a));

    let b = Rc::clone(&a);   // b is a second owner - count goes to 2
    println!("count after b:          {}", Rc::strong_count(&a));

    {
        let c = Rc::clone(&a);  // a third owner, in an inner scope
        println!("count after c:          {}", Rc::strong_count(&a));
    } // c dropped here - count goes back down

    println!("count after c dropped:   {}", Rc::strong_count(&a));
    println!("value still alive:       {}", a);
}
```
```console
$ cargo run
count after creating a: 1
count after b:          2
count after c:          3
count after c dropped:   2
value still alive:       shared data
```
*What just happened:* `Rc::new` created the value with a count of 1. Each `Rc::clone` made another owner and bumped the count - `a`, `b`, and `c` all own the same `String`, with no copies of its bytes. When `c` went out of scope, its drop brought the count back to 2. The `String` frees only when the *last* `Rc` drops. `Rc::strong_count(&a)` lets you watch the bookkeeping happen.

The shared-ownership picture: three `Rc` handles, one heap value, one count.

```mermaid
flowchart LR
  A[Rc a] --> V["String 'shared data'<br/>strong_count = 3"]
  B[Rc b] --> V
  C[Rc c] --> V
```

⚠️ **`Rc<T>` is single-threaded only.** Its counter is an ordinary integer with no synchronization, so two threads bumping it at once would corrupt the count and cause double-frees or leaks. `Rc` is deliberately not safe to send between threads, and the compiler rejects any attempt to share one across threads. For that you need its thread-safe sibling, next.

## `Arc<T>` - the same idea, across threads

`Arc<T>` ("atomic reference counted") is `Rc<T>` with one change: the count updates using **atomic** operations, safe from multiple threads at once. The API is identical - `Arc::new`, `Arc::clone`, `Arc::strong_count` - so mentally it's "`Rc` you're allowed to share between threads."

```rust
use std::sync::Arc;
use std::thread;

fn main() {
    let data = Arc::new(vec![1, 2, 3]);
    let mut handles = vec![];

    for id in 0..3 {
        let data = Arc::clone(&data);   // each thread gets its own owning handle
        handles.push(thread::spawn(move || {
            println!("thread {id} sees {:?}", data);
        }));
    }

    for h in handles {
        h.join().unwrap();
    }
}
```
```console
$ cargo run
thread 0 sees [1, 2, 3]
thread 2 sees [1, 2, 3]
thread 1 sees [1, 2, 3]
```
*What just happened:* Each spawned thread received its own `Arc` clone (a cheap count bump), so all three share the one `Vec` without copying it. The atomic counter stays correct even though the threads run concurrently and finish in unpredictable order. The `Vec` frees only after the last thread drops its handle. We'll go deep on threads in [Phase 14](14-fearless-concurrency.md); for now, the takeaway is just *which* refcounted pointer to pick.

💡 **When to pay for `Arc` vs `Rc`.** Atomic operations cost slightly more than plain increments: use `Rc` for single-threaded sharing, `Arc` only when the value crosses thread boundaries. Don't reach for `Arc` "just in case" - the compiler will reject an `Rc` you try to send to a thread the moment you actually need one.

## Interior mutability with `RefCell<T>`

Notice what `Rc` *can't* do: it gives you shared ownership, but everything you get out of it is immutable - many owners, no writers, the borrow rule holding firm. So how do you get *shared, mutable* state, like a tree node that needs to update a value several owners can see? You need to bend the other rule: mutate through a shared reference. That's **interior mutability**.

📝 **Interior mutability** - a pattern where you mutate data even though you only hold a *shared* (`&`) reference to it. `RefCell<T>` makes this safe by **moving the borrow check from compile time to runtime**: the same rule (many readers or one writer) still applies, just checked by counters inside the `RefCell` as the program runs instead of by the compiler beforehand.

You ask for access with two methods: `.borrow()` gives a shared read handle, `.borrow_mut()` an exclusive write handle. Break the rules - ask for a `borrow_mut` while another borrow is live - and instead of a compile error you get a **runtime panic**.

```rust
use std::cell::RefCell;

fn main() {
    let log = RefCell::new(Vec::new());

    // We only hold `&log`, yet we can push into the Vec:
    log.borrow_mut().push("first");
    log.borrow_mut().push("second");

    println!("{:?}", log.borrow());   // a read borrow
}
```
```console
$ cargo run
["first", "second"]
```
*What just happened:* `log` is not declared `mut` and we never took a `&mut` to it - yet we mutated the `Vec` inside. Each `borrow_mut()` handed out a temporary exclusive write handle that ended at the semicolon, so the next one was free to take its turn; `RefCell` tracked this at runtime and saw no overlap.

Now the sharp edge: hold two conflicting borrows at once and it doesn't refuse to compile - it *panics*:

```rust
use std::cell::RefCell;

fn main() {
    let data = RefCell::new(5);

    let read = data.borrow();          // a shared borrow, still alive...
    let mut write = data.borrow_mut(); // ...and now an exclusive one. Conflict!

    *write += 1;
    println!("{} {}", read, write);
}
```
```console
$ cargo run
thread 'main' panicked at src/main.rs:7:26:
RefCell already borrowed
```
*What just happened:* The `read` borrow was still alive when we asked for `write`, violating "many readers *or* one writer." A plain `&`/`&mut` would have failed to compile (`error[E0502]`); `RefCell` instead let it compile and caught it at runtime, panicking with a `BorrowMutError` (the message reads `RefCell already borrowed`). ⚠️ **This is the price of `RefCell`:** a compile-time guarantee becomes a runtime check, so a violation the compiler would have caught for free now becomes a crash that only shows up when that path runs. Keep your borrows short and scoped.

**The classic combo: `Rc<RefCell<T>>`.** Stack the two and you get what neither gives alone: `Rc` provides multiple owners, `RefCell` lets each mutate the shared value. This pair is the standard Rust recipe for shared mutable state in single-threaded code (graphs, trees with back-references, observer-style structures).

```rust
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let shared = Rc::new(RefCell::new(vec![1, 2, 3]));

    let owner_a = Rc::clone(&shared);
    let owner_b = Rc::clone(&shared);

    owner_a.borrow_mut().push(4);   // mutate through one owner
    owner_b.borrow_mut().push(5);   // ...and another

    println!("{:?}", shared.borrow());   // everyone sees the same updated Vec
}
```
```console
$ cargo run
[1, 2, 3, 4, 5]
```
*What just happened:* `owner_a` and `owner_b` are two owners of the *same* `RefCell<Vec<i32>>` (`Rc` made the sharing legal). Each called `borrow_mut()` to push into the shared `Vec`, and because the borrows didn't overlap, `RefCell` allowed both - the final read through `shared` shows one `Vec` that both owners mutated. `Rc<RefCell<T>>`: shared *and* mutable, single-threaded. (The thread-safe version is `Arc<Mutex<T>>`, from [Phase 14](14-fearless-concurrency.md).)

There's also **`Cell<T>`**, a lighter cousin of `RefCell` for `Copy` types like numbers and booleans. Instead of handing out borrows, it works by *moving values in and out* - `.get()` copies the value out, `.set(x)` replaces it - so there are no borrow handles to conflict and no runtime panic possible. Reach for `Cell` for a small `Copy` value; reach for `RefCell` when the value is bigger or non-`Copy` and you need a real borrow of it.

## The machinery underneath: `Deref` and `Drop`

Two traits make all of this work, and knowing their names demystifies the whole category.

**`Deref` - why `*` and method calls "see through" a smart pointer.** When you write `*my_box` to get at the value inside, or call `some_string.len()` even though `String` is a wrapper, that's the `Deref` trait. It defines what `*` does, and Rust uses it for *deref coercion*: automatically turning a `&Box<T>` into a `&T`, or a `&String` into a `&str`, so your smart pointer behaves like the value it wraps - why a `Box<T>` is so transparent that you mostly forget it's there.

```rust
fn main() {
    let boxed = Box::new(String::from("hello"));

    // Deref lets us call String methods straight through the Box,
    // and *boxed gets at the String itself:
    println!("len via Box: {}", boxed.len());
    println!("upper: {}", (*boxed).to_uppercase());
}
```
```console
$ cargo run
len via Box: 5
upper: HELLO
```
*What just happened:* `boxed` is a `Box<String>`, but `boxed.len()` worked as if it were a plain `String` - `Deref` coercion reached through the `Box` to the `String` (and `String`'s own `Deref` reaches further, to `&str`). `*boxed` explicitly dereferenced to the `String` value. This automatic see-through behavior is what makes smart pointers feel like the values they hold, not wrappers you constantly unpack.

**`Drop` - custom cleanup when a value goes out of scope.** The `Drop` trait defines code that runs automatically the moment a value is dropped. This is **RAII** (resource acquisition is initialization): tie a resource - heap memory, a file handle, a lock - to a value's lifetime, and its release is guaranteed even on an early return or a panic. It's how `Box` frees its heap, `Rc` decrements its count, and a file closes itself. You can implement it for your own types too:

```rust
struct Guard {
    name: String,
}

impl Drop for Guard {
    fn drop(&mut self) {
        println!("dropping Guard({})", self.name);
    }
}

fn main() {
    let _a = Guard { name: "a".into() };
    {
        let _b = Guard { name: "b".into() };
        println!("inner scope");
    } // _b dropped here
    println!("outer scope");
} // _a dropped here
```
```console
$ cargo run
inner scope
dropping Guard(b)
outer scope
dropping Guard(a)
```
*What just happened:* Each `Guard`'s `drop` ran automatically the moment it went out of scope - `_b` at the end of the inner block, `_a` at the end of `main`. Note the order: values drop in *reverse* order of creation (last in, first out). You never called `drop` yourself; the compiler inserted the calls. This is the same mechanism that frees every `Box`, decrements every `Rc`, and releases every lock in the language.

💡 **How to choose, in one breath.** `Box<T>` for one value on the heap (or a recursive type). `Rc<T>` / `Arc<T>` when a value needs **multiple owners** - `Rc` single-threaded, `Arc` across threads. `RefCell<T>` (often inside an `Rc`) when you need to **mutate through a shared reference**. Most code needs none of these; reach for a smart pointer only when the single-owner rule genuinely gets in your way, and pick the one that bends the *least*.

## Recap

1. **Smart pointers** are structs that act like pointers but add behavior (heap allocation, reference counting, runtime borrow checks, custom cleanup) - they bend the ownership rules in controlled, documented ways rather than breaking them.
2. **`Box<T>`** puts one value on the heap with normal single-owner, compile-time-borrow semantics; it's required for recursive types like cons lists and tree nodes (`error[E0072]` without it).
3. **`Rc<T>`** gives **multiple owners** via a reference count; `Rc::clone` is a cheap count bump (not a deep copy), and the value is freed when the count hits zero. ⚠️ Single-threaded only - use **`Arc<T>`** to share across threads.
4. **`RefCell<T>`** enables **interior mutability**: mutate through a shared reference by moving the borrow check to runtime. Break the rules and it **panics** (`BorrowMutError`) instead of failing to compile. `Rc<RefCell<T>>` is the standard single-threaded shared-mutable combo; `Cell<T>` is the lightweight option for `Copy` values.
5. **`Deref`** is why `*` and method calls see through a smart pointer (and powers deref coercion like `Box<String>` → `String` → `&str`); **`Drop`** runs cleanup automatically when a value's scope ends (RAII) - the mechanism behind every freed `Box`, decremented `Rc`, and closed file.
6. **Choosing:** `Box` for single-owner heap, `Rc`/`Arc` for shared ownership, `RefCell` for mutation through a shared reference - and most code needs none of them. Pick the pointer that bends the fewest rules.

## Quick check

Lock in the distinction that matters most - which pointer relaxes which rule:

```quiz
[
  {
    "q": "What does `Rc::clone(&a)` actually do?",
    "choices": [
      "Increments the reference count and returns another owning handle to the same data - no deep copy",
      "Makes a full, independent copy of the underlying value",
      "Moves ownership out of `a`, leaving it invalid",
      "Spawns a thread that shares the value"
    ],
    "answer": 0,
    "explain": "`Rc::clone` is cheap: it bumps the reference count and hands back another owner pointing at the *same* heap value. It does not copy the data. The value is freed only when the last `Rc` is dropped (count reaches zero)."
  },
  {
    "q": "You hold a `RefCell<T>`, call `.borrow()`, and then call `.borrow_mut()` while the first borrow is still alive. What happens?",
    "choices": [
      "The program panics at runtime with a BorrowMutError",
      "It fails to compile, just like a `&`/`&mut` conflict would",
      "Both borrows succeed silently - RefCell allows overlap",
      "The first borrow is automatically dropped to make room"
    ],
    "answer": 0,
    "explain": "`RefCell` moves borrow checking to runtime. The rule (many readers OR one writer) is still enforced, but a violation panics while the program runs instead of being caught by the compiler. That runtime panic is the price you pay for interior mutability."
  },
  {
    "q": "Why does a recursive `enum List { Cons(i32, List), Nil }` fail to compile, and how does `Box` fix it?",
    "choices": [
      "The type has infinite size; `Box<List>` is a fixed-size pointer to the heap, which breaks the recursion",
      "Enums can't be recursive at all; `Box` makes the enum into a struct",
      "`i32` is too small; `Box` upgrades it to a larger integer",
      "The compiler needs a `Drop` impl; `Box` provides one automatically"
    ],
    "answer": 0,
    "explain": "Laying out `List` requires its size, but a `List` containing a `List` containing a `List`... is infinite. `Box<List>` stores the next node on the heap and is itself a fixed-size pointer, so the type has a finite, known size and compiles."
  }
]
```


---

# Error Handling, Deep - Result, ?, and Custom Errors

Back in [Phase 7](07-errors-and-io.md) you met the two enums that carry failure in Rust - `Result<T, E>` and `Option<T>` - learned to crack them open with `match`, and reached for `?` to bubble errors up without ceremony. That was enough to read a file and survive. But the moment your program grows past a single function, a real question shows up: *one function calls `parse`, which fails with a `ParseIntError`; another reads a file, which fails with an `io::Error`. They're different error types. How does a single `?` deal with both?*

The answer is the spine of this phase. We'll look under `?` and find its hidden job - converting errors - then use that to design error types of your own, and finally meet the two crates (`thiserror` and `anyhow`) that nearly every Rust project reaches for. By the end, errors stop being a thing you patch around and become something you *design*.

## What `?` really does

You know the surface behavior: `?` on an `Ok` hands you the value and keeps going; `?` on an `Err` returns early. But a third move hides in there.

📝 **The `?` operator, precisely.** When you write `expr?`:
- If `expr` is `Ok(v)` (or `Some(v)`), the whole expression evaluates to `v` and execution continues.
- If `expr` is `Err(e)` (or `None`), `?` **returns from the current function** - but first, for `Result`, it runs the error through `From::from(e)`, converting it into your function's declared error type.

That last clause is the part Phase 7 skipped. `?` is not "return the error" - it's "convert the error to my return type's error, *then* return it."

```rust
use std::num::ParseIntError;

fn double_the_input(s: &str) -> Result<i32, ParseIntError> {
    let n = s.parse::<i32>()?;   // parse returns Result<i32, ParseIntError>
    Ok(n * 2)
}

fn main() {
    println!("{:?}", double_the_input("21"));
    println!("{:?}", double_the_input("oops"));
}
```
```console
$ cargo run
Ok(42)
Err(ParseIntError { kind: InvalidDigit })
```
*What just happened:* `s.parse::<i32>()` returns a `Result<i32, ParseIntError>`. On `"21"` the `?` unwrapped `Ok(21)` to `21`, and we doubled it. On `"oops"` the `?` saw `Err(...)` and returned that error straight out of `double_the_input`. Here the error type going *in* already matches the type coming *out*, so the conversion step did nothing visible. The interesting case is when they *don't* match - exactly where `From` earns its keep.

💡 **Key point.** `?` works in any function whose return type is a `Result` (or, separately, an `Option`) - not only in `main`. It's purely a control-flow plus conversion shortcut. If the function can't carry the error out, the compiler stops you, because `?`'s entire purpose is to *return* that error.

## The `From` trait: how one `?` swallows many error types

Here's the problem `From` solves. Imagine a function that both reads a file *and* parses a number from it. The read can fail with `std::io::Error`; the parse can fail with `ParseIntError`. Two error types, one function - what does it return?

The Rust answer: define *your own* error type, then teach Rust how to convert each underlying error into it by implementing the `From` trait - the same trait behind `.into()` and type conversions. Once `From<io::Error>` and `From<ParseIntError>` both exist for your error type, `?` can convert *either* one automatically, and a single error type flows out.

```rust
use std::num::ParseIntError;

#[derive(Debug)]
enum ConfigError {
    Io(std::io::Error),
    Parse(ParseIntError),
}

// Teach `?` how to turn an io::Error into a ConfigError.
impl From<std::io::Error> for ConfigError {
    fn from(e: std::io::Error) -> Self {
        ConfigError::Io(e)
    }
}

// And how to turn a ParseIntError into a ConfigError.
impl From<ParseIntError> for ConfigError {
    fn from(e: ParseIntError) -> Self {
        ConfigError::Parse(e)
    }
}

fn read_port() -> Result<u16, ConfigError> {
    let text = std::fs::read_to_string("port.txt")?;   // io::Error -> ConfigError
    let port = text.trim().parse::<u16>()?;            // ParseIntError -> ConfigError
    Ok(port)
}

fn main() {
    match read_port() {
        Ok(p) => println!("port is {}", p),
        Err(e) => println!("config error: {:?}", e),
    }
}
```
```console
$ cargo run
config error: Io(Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." })
```
*What just happened:* Two `?` operators, two *different* underlying error types, yet `read_port` declares a single error type: `ConfigError`. The first `?` hit a missing file, so it called `ConfigError::from(io_error)` - which we implemented to wrap it in the `Io` variant - and returned that. Had the file existed but contained `"not-a-number"`, the second `?` would have called `ConfigError::from(parse_error)` and returned `Parse` instead. The conversion is invisible at the call site; the `From` impls do the quiet work.

💡 **This is the glue.** Every time `?` against a foreign error type "just fits," there's a `From` impl making it fit - either the standard library's or your own. `From` is the trait that lets `?` stay a single character while juggling a whole zoo of error types underneath.

## Custom error enums done right

The enum above worked, but printing it with `{:?}` gave a developer-facing debug dump, not a sentence a human wants to read. A *proper* domain error does three things: enumerates the failure modes as variants, implements `Display` for a clean message, and implements the standard `Error` trait so it slots into the rest of the ecosystem (logging, `Box<dyn Error>`, other people's `?`).

```rust
use std::fmt;

#[derive(Debug)]
enum AppError {
    NotFound,
    Invalid(String),
    Io(std::io::Error),
}

// Display = the human-readable message.
impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AppError::NotFound => write!(f, "the requested item was not found"),
            AppError::Invalid(what) => write!(f, "invalid input: {}", what),
            AppError::Io(e) => write!(f, "I/O failure: {}", e),
        }
    }
}

// Opting into the standard Error trait makes this a "real" error type.
impl std::error::Error for AppError {}

fn load(id: i32) -> Result<String, AppError> {
    match id {
        0 => Err(AppError::NotFound),
        n if n < 0 => Err(AppError::Invalid(format!("id {} is negative", n))),
        n => Ok(format!("item #{}", n)),
    }
}

fn main() {
    for id in [-1, 0, 7] {
        match load(id) {
            Ok(item) => println!("loaded {}", item),
            Err(e) => println!("error: {}", e),   // uses Display, not Debug
        }
    }
}
```
```console
$ cargo run
error: invalid input: id -1 is negative
error: the requested item was not found
loaded item #7
```
*What just happened:* `AppError` is an enum with one variant per way the operation can go wrong - notice `Invalid` and `Io` carry data (the offending input, the underlying I/O error), the "make illegal states unrepresentable" idea from [Phase 9](09-idioms-and-gotchas.md). The `Display` impl turns each variant into a sentence, so printing with `{}` reads like English instead of a struct dump. Implementing `std::error::Error` is the formal handshake that says "this is an error type" - it's what lets `AppError` be returned as `Box<dyn Error>`, logged by libraries, or wrapped by other errors. Callers can still `match` on the variants to react differently to `NotFound` versus `Invalid`.

⚠️ **The boilerplate adds up fast.** That's a lot of hand-written code for one error type: a `Display` arm per variant, the `Error` impl, and - if you want `?` to convert into it - a `From` impl per source error too. A few error types in and you're writing the same shapes over and over, editing the `Display` match by hand for every new variant. This pain is exactly why the next section exists.

## `thiserror` and `anyhow`: the two crates everyone reaches for

Almost no real Rust project hand-writes `Display` and `From` impls the way we just did. Two small crates eliminate the boilerplate, split along a clear line.

📝 **The rule of thumb.** Use **`thiserror`** when writing a **library** (or any code where callers need to *match on specific error variants*) - it derives the clean enum for you. Use **`anyhow`** when writing an **application** (a binary, a CLI, a service) that mostly wants to say "something failed, here's some context, propagate it" without defining a bespoke type for every failure.

### `thiserror` - derive the enum, skip the boilerplate

`thiserror` is a derive macro: you write the enum and annotate it, and the macro generates the `Display` impl (from your `#[error("...")]` strings) and the `From` impls (from `#[from]`) we wrote by hand above.

```rust
use thiserror::Error;

#[derive(Error, Debug)]
enum AppError {
    #[error("the requested item was not found")]
    NotFound,

    #[error("invalid input: {0}")]
    Invalid(String),

    #[error("I/O failure: {0}")]
    Io(#[from] std::io::Error),   // #[from] also generates From<io::Error>
}

fn load_file(path: &str) -> Result<String, AppError> {
    let text = std::fs::read_to_string(path)?;   // io::Error -> AppError, for free
    if text.is_empty() {
        return Err(AppError::Invalid("file was empty".into()));
    }
    Ok(text)
}

fn main() {
    match load_file("missing.txt") {
        Ok(t) => println!("read {} bytes", t.len()),
        Err(e) => println!("error: {}", e),
    }
}
```
```console
$ cargo run
error: I/O failure: The system cannot find the file specified. (os error 2)
```
*What just happened:* This is the *same* `AppError` as the previous section - same variants, same behavior - but every line of `Display` and `From` boilerplate is gone. The `#[error("...")]` attributes became the `Display` impl (`{0}` interpolates the variant's first field). `#[from]` on the `Io` variant generated `impl From<std::io::Error> for AppError`, which is why `?` in `load_file` silently converts the I/O error. You still get a precise, matchable enum - `thiserror` just wrote the tedious parts, which is why it's the default for libraries.

### `anyhow` - one error type, easy context

Application code often doesn't care *which* of fifteen error types occurred - it cares that something failed, wants a breadcrumb of context, and wants to print it and move on. `anyhow` gives you a single catch-all error type (`anyhow::Error`) that any standard error converts into automatically, plus `.context()` to attach a human note as the error travels up.

```rust
use anyhow::{Context, Result};   // anyhow::Result<T> == Result<T, anyhow::Error>

fn load_settings(path: &str) -> Result<u16> {
    let text = std::fs::read_to_string(path)
        .with_context(|| format!("reading settings from {}", path))?;
    let port: u16 = text
        .trim()
        .parse()
        .context("settings file must contain a port number")?;
    Ok(port)
}

fn main() -> Result<()> {
    let port = load_settings("settings.txt")?;
    println!("listening on {}", port);
    Ok(())
}
```
```console
$ cargo run
Error: reading settings from settings.txt

Caused by:
    The system cannot find the file specified. (os error 2)
```
*What just happened:* `load_settings` never defines an error type at all - `anyhow::Result<u16>` means "a `u16`, or *any* error." The `?` operators accept the `io::Error` and `ParseIntError` directly, because `anyhow::Error` absorbs anything implementing the standard `Error` trait (no `From` impls to write). The `.with_context(...)` / `.context(...)` calls attach a readable note, and `anyhow` stitches them into the "Error / Caused by" chain above - so you get the high-level intent *and* the root cause. This is the ergonomic sweet spot for binaries: maximum signal, near-zero ceremony.

💡 **In practice they pair up.** A common setup: library crates expose `thiserror` enums so consumers can match precisely, and the top-level application crate uses `anyhow` to collect them, add context, and report. `thiserror` for people who *handle* your errors; `anyhow` for the program that just needs to *surface* them.

## `Option` combinators, and when a panic is actually fine

Two loose ends from Phase 7's basics, both about choosing the *lightest correct tool*.

First, combinators. You met `Result`/`Option` combinators briefly in [Phase 9](09-idioms-and-gotchas.md); they shine just as much at avoiding "match towers" on `Option`. Instead of nesting `match` after `match` to transform a maybe-value, chain the transformation:

```rust
fn main() {
    let raw = vec!["10", "x", "30"];

    // For each string: parse it (-> Result), turn failure into None, keep going.
    let total: i32 = raw
        .iter()
        .map(|s| s.parse::<i32>().ok())   // Result -> Option
        .map(|opt| opt.unwrap_or(0))      // None -> 0, Some(n) -> n
        .sum();

    // .ok_or turns an Option into a Result with an error of your choosing:
    let first: Result<&&str, &str> = raw.first().ok_or("the list was empty");

    println!("total = {}", total);
    println!("first = {:?}", first);
}
```
```console
$ cargo run
total = 40
first = Ok("10")
```
*What just happened:* No `match` in sight. `.ok()` converts each `Result` into an `Option` (dropping the error), `.unwrap_or(0)` substitutes a default for the `None`s, and `.sum()` adds what's left - so the un-parseable `"x"` quietly became `0` and the total is `40`. Separately, `.ok_or(...)` does the reverse: it turns an `Option` into a `Result`, letting you *upgrade* an absence into a real error with a message. The combinators worth knowing: `.map` (transform the value), `.and_then` (chain another fallible step), `.ok_or` (`Option` → `Result`), and `.unwrap_or` / `.unwrap_or_else` (supply a fallback). Reach for these for simple cases; save `match` for when each branch genuinely does something different.

Second, the panic question. Phase 7 said `.unwrap()` is a landmine in production - true. But "never panic" is the wrong lesson. Panicking is the *right* call in specific places:

⚠️ **When `panic!` / `.unwrap()` / `.expect()` are acceptable.**
- ✅ **Tests.** A failed assumption *should* crash the test. `.unwrap()` everywhere in test code is idiomatic, not sloppy.
- ✅ **Prototypes and throwaway scripts**, where error plumbing would obscure the idea you're sketching.
- ✅ **Truly impossible cases you can prove** - e.g. `"42".parse::<i32>().unwrap()` on a literal you wrote yourself. Even then, prefer `.expect("hard-coded constant, cannot fail")` so the message documents *why* it's safe.
- ✅ **Broken invariants** - a state that means your own logic is wrong (an empty list you guaranteed wouldn't be). A panic here is a loud bug report, which is what you want.
- ❌ **Everything else** - anything that can fail because of the *outside world* (files, network, user input, parsed data) is an *expected* failure. Return a `Result` and let the caller decide.

The dividing line is simple: **`Result` for failures you expect, `panic!` for bugs you don't.** A missing config file is expected - return a `Result`. A counter going negative when you proved it can't - that's a bug, panic and find out.

## Recap

1. **`?` does three things, not two:** unwrap on `Ok`/`Some`, return-early on `Err`/`None`, and - for `Result` - convert the error via `From` on the way out. It works in any function whose return type can carry the error.
2. **The `From` trait is the glue.** Implementing `From<SourceError>` for your error type is what lets a single `?` absorb many different underlying error types and funnel them into one.
3. **A proper custom error** is an enum (one variant per failure mode, carrying relevant data) that implements `Display` for a human message and `std::error::Error` to join the ecosystem.
4. **`thiserror` derives all that boilerplate** for libraries (`#[error("...")]` for `Display`, `#[from]` for `From`), giving callers a clean, matchable enum.
5. **`anyhow` is the application default:** one catch-all `anyhow::Error`, automatic conversion from any standard error, and `.context(...)` to attach breadcrumbs. Rule of thumb - `thiserror` for libraries, `anyhow` for apps.
6. **Combinators (`.map`, `.and_then`, `.ok_or`, `.unwrap_or`) beat match towers** for simple transforms; and **`panic!`/`.unwrap()` are fine in tests, prototypes, and proven-impossible cases** - but expected, outside-world failures belong in a `Result`.

## Quick check

Test yourself on the idea that ties this phase together - the hidden conversion inside `?`, and the crate split.

```quiz
[
  {
    "q": "Beyond unwrapping `Ok` and returning early on `Err`, what extra thing does `?` do to the error before returning it?",
    "choices": [
      "Converts it into the function's declared error type via the `From` trait",
      "Logs it to standard error automatically",
      "Wraps it in a `panic!` so the program crashes",
      "Discards the error and substitutes a default value"
    ],
    "answer": 0,
    "explain": "On an `Err`, `?` calls `From::from` on the error to convert it into the current function's error type, then returns it. That conversion is what lets one `?` handle many different underlying error types - as long as a `From` impl exists for each."
  },
  {
    "q": "You're writing a reusable library and want callers to be able to `match` on specific failure variants. Which approach fits best?",
    "choices": [
      "A `thiserror`-derived error enum",
      "`anyhow::Error` everywhere, since it absorbs any error",
      "Return `String` error messages so callers can read them",
      "`.unwrap()` on everything and let the caller catch the panic"
    ],
    "answer": 0,
    "explain": "Libraries should expose a concrete, matchable error type so consumers can react to specific cases. `thiserror` derives the `Display` and `From` boilerplate for such an enum. `anyhow`'s catch-all type is meant for applications, where callers usually just surface the error rather than match on it."
  },
  {
    "q": "Which situation is the *right* place to use `.unwrap()`?",
    "choices": [
      "Parsing a hard-coded literal you wrote yourself, where failure is provably impossible",
      "Reading a config file that a user supplies",
      "Making a network request that could time out",
      "Parsing input typed by the user at runtime"
    ],
    "answer": 0,
    "explain": "`.unwrap()` (ideally `.expect(\"why\")`) is fine when failure is genuinely impossible, such as parsing a constant you control - also in tests and throwaway scripts. The other three involve the outside world (files, network, user input), where failure is expected and should be returned as a `Result` for the caller to handle."
  }
]
```


---

# Fearless Concurrency - Threads the Compiler Keeps Safe

Concurrency is where most languages quietly betray you. You write code that works on your laptop, ship it, and three weeks later a customer hits a race condition that only appears under load - two threads touched the same data at the same time, and the result was garbage. Those bugs are notoriously hard to reproduce and harder to fix, because they depend on timing.

Rust's pitch - and it's not marketing fluff - is **fearless concurrency**: you can write multithreaded code and trust that an entire category of those bugs *cannot reach production*, because the compiler refuses to build them. There's no new safety system to learn here - it's the same ownership and borrowing rules from [Phase 6](06-ownership-and-borrowing.md), applied across threads. "Many readers or one writer, never both" is exactly what prevents a data race, and the borrow checker already enforces it. This phase is mostly about meeting the few tools that let you share data across threads *while keeping those rules intact*.

📝 **Data race** - two or more threads access the same memory at the same time, at least one of them writing, with no synchronization. The result is undefined: torn values, lost updates, corruption. This is the specific bug class Rust eliminates at compile time.

## Spawning threads

**What it actually is.** `std::thread::spawn` starts a new OS thread running a closure. It hands back a `JoinHandle` - a receipt you can use to wait for that thread to finish and collect its result. Threads run *concurrently*, so the order their output appears in is not guaranteed.

```rust
use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..=3 {
            println!("  spawned: {i}");
        }
    });

    for i in 1..=3 {
        println!("main: {i}");
    }

    handle.join().unwrap();   // wait for the spawned thread to finish
}
```
```console
$ cargo run
main: 1
main: 2
  spawned: 1
main: 3
  spawned: 2
  spawned: 3
```
*What just happened:* `spawn` launched a second thread that ran alongside `main`. Both loops printed at the same time, so their lines interleaved - the exact interleaving changes from run to run, so your output order will differ. `handle.join()` blocked `main` until the spawned thread finished; without it, `main` could return and tear down the whole program before the spawned thread ever printed.

⚠️ **`.join()` matters.** When `main` returns, the process ends - any still-running threads are killed mid-stride. If you spawn work you want to complete, hold the `JoinHandle` and `.join()` it.

Now the more useful case: giving a thread some data to work with. A closure using a value from the surrounding scope needs to *own* it, because the thread may outlive the function that spawned it. Force that with `move`.

```rust
use std::thread;

fn main() {
    let greeting = String::from("hello from the main scope");

    let handle = thread::spawn(move || {
        // `move` gives this closure ownership of `greeting`
        println!("{greeting}");
    });

    handle.join().unwrap();
}
```
```console
$ cargo run
hello from the main scope
```
*What just happened:* `move` transferred ownership of `greeting` *into* the closure, so the spawned thread owns it outright - move semantics from Phase 6 doing exactly its job. The thread can't borrow `greeting` from `main`, because `main` might end first and drop it, leaving the thread holding a dangling reference. By *moving* it, ownership goes to the thread and the problem disappears - the compiler insists on `move` here precisely so that bug can't happen.

## Why data races can't compile

Here's the headline, demonstrated. Suppose two threads try to mutate the same vector at once, the naive way - no synchronization, just shared mutable access. In C, C++, or Python this compiles and runs, a data race waiting to corrupt your data. In Rust, it doesn't get past the compiler.

```rust
use std::thread;

fn main() {
    let mut data = vec![1, 2, 3];

    let handle = thread::spawn(move || {
        data.push(4);          // thread 1 mutates `data`
    });

    data.push(5);              // main also mutates `data` - two writers!
    handle.join().unwrap();
}
```
```console
$ cargo build
error[E0382]: borrow of moved value: `data`
  --> src/main.rs:10:5
   |
4  |     let mut data = vec![1, 2, 3];
   |         -------- move occurs because `data` has type `Vec<i32>`, which does not implement the `Copy` trait
6  |     let handle = thread::spawn(move || {
   |                                ------- value moved into closure here
7  |         data.push(4);
   |         ---- variable moved due to use in closure
...
10 |     data.push(5);
   |     ^^^^ value used here after move
```
*What just happened:* The `move` closure took ownership of `data` for the thread, so `main` no longer owns it - `data.push(5)` in `main` is a use-after-move, the same error from Phase 6. The compiler won't let two owners mutate the same value. There's no way to express "both threads freely write to this" without a synchronization tool - that's the whole point: the *only* way forward is to make the sharing safe.

💡 **This is the deal.** The race conditions other languages ship to production and chase with logging and prayer, Rust catches at build time - using nothing more than the ownership rules you already know.

## Sharing mutable state: `Arc<Mutex<T>>`

Sometimes you genuinely need multiple threads to share *and* mutate the same data. Rust makes you do it safely, with two pieces that stack together:

- **`Arc<T>`** - an *atomically reference-counted* pointer (the thread-safe sibling of `Rc` from [Phase 12](12-smart-pointers.md)). It lets several threads *co-own* the same value; the data drops only when the last owner goes away.
- **`Mutex<T>`** - a *mutual-exclusion lock*. To touch the data inside, a thread must call `.lock()`, which blocks until it's the *only* thread holding the lock.

📝 **Mutex** - a lock that guarantees only one thread accesses the protected data at a time. `.lock()` returns a guard; when the guard goes out of scope, the lock releases automatically - no manual unlock to forget.

`Arc` answers "who owns this?" (everyone, jointly). `Mutex` answers "who may write to it *right now*?" (whoever holds the lock). Together, `Arc<Mutex<T>>` is the standard way to share mutable state across threads.

```mermaid
flowchart LR
  A[Thread A] -->|.lock| M["Arc&lt;Mutex&lt;i32&gt;&gt;"]
  B[Thread B] -->|.lock| M
  C[Thread C] -->|.lock| M
  M --> D[one writer<br/>at a time]
```

Ten threads each bump a shared counter below. Without the `Mutex`, that's the classic data race; with it, every increment is safe:

```rust
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);   // a new owning handle for this thread
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();   // wait for the lock
            *num += 1;                                // safe: we hold it exclusively
        });                                          // lock releases as `num` drops
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("final count: {}", *counter.lock().unwrap());
}
```
```console
$ cargo run
final count: 10
```
*What just happened:* `Arc::clone` made ten owning handles to the *same* `Mutex` - cheap, just a reference-count bump, no data copied. Each thread called `.lock()`, which handed it exclusive access and blocked any other thread until the guard (`num`) dropped at the end of the closure. Because only one thread ever mutates the counter at a time, the final value is reliably `10` - run it a thousand times and it's `10` every time.

⚠️ **Rust prevents data races, not deadlocks.** A `Mutex` can still deadlock: if thread A holds lock 1 and waits for lock 2, while thread B holds lock 2 and waits for lock 1, both wait forever. That's a *logic* bug, not a data race, and the compiler can't catch it. The guardrails: always acquire multiple locks in the same order everywhere, and keep locked sections short.

## `Send` and `Sync`: the traits behind the magic

How does the compiler *know* whether a type is safe to move to a thread or share between threads? Two marker traits, applied automatically.

📝 **`Send`** - a type is `Send` if it's safe to *move* to another thread. `String`, `Vec<T>`, `Arc<T>` are all `Send`. This is what made the `move` closure earlier legal.

📝 **`Sync`** - a type is `Sync` if it's safe to *share by reference* across threads (`&T` can be handed to multiple threads at once). `Mutex<T>` is `Sync` because its lock guarantees safe access.

These are *marker* traits - they carry no methods, just labels the compiler reads to decide what's allowed. Almost every type is automatically `Send` and `Sync` because it's built from parts that are. You rarely write either yourself; you just feel them when something *isn't*.

The sharpest example is exactly why `Arc` exists. `Rc<T>` from Phase 12 is **not** `Send` - its reference count is a plain integer, so two threads bumping it at once would race and corrupt the count. Try to send an `Rc` to a thread and the compiler stops you:

```rust
use std::rc::Rc;
use std::thread;

fn main() {
    let data = Rc::new(42);
    let handle = thread::spawn(move || {
        println!("{}", data);   // moving a non-Send Rc into a thread
    });
    handle.join().unwrap();
}
```
```console
$ cargo build
error[E0277]: `Rc<i32>` cannot be sent between threads safely
 --> src/main.rs:6:32
  |
6 |     let handle = thread::spawn(move || {
  |                                ^^^^^^^ `Rc<i32>` cannot be sent between threads safely
  |
  = help: the trait `Send` is not implemented for `Rc<i32>`
note: required because it's used within this closure
```
*What just happened:* `Rc` isn't `Send`, so the closure that captures it isn't `Send` either, and `thread::spawn` requires its closure to be `Send`. The compiler traced the whole chain and refused. The fix is `Arc`, whose reference count uses atomic operations that *are* safe across threads - the entire reason `Arc` exists alongside `Rc`. You never mentioned either trait, but they decided what was allowed.

## Channels: share by communicating

`Arc<Mutex<T>>` shares state. There's an often-cleaner alternative: don't share at all - *pass messages*. One thread sends values, another receives them, and ownership of each value moves across the boundary. No locks, no shared mutable state to reason about.

The standard library gives you this through `std::sync::mpsc` - **m**ultiple **p**roducer, **s**ingle **c**onsumer. `channel()` returns a `(Sender, Receiver)` pair: the sender's `.send()` pushes a value in, the receiver pulls values out (iterable, ending when all senders are dropped).

```rust
use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        for word in ["the", "missing", "manual"] {
            tx.send(String::from(word)).unwrap();   // ownership moves into the channel
        }
        // tx drops here, which closes the channel
    });

    for received in rx {            // iterate until the channel closes
        println!("got: {received}");
    }
}
```
```console
$ cargo run
got: the
got: missing
got: manual
```
*What just happened:* The spawned thread `send`s three strings down the channel; ownership of each `String` *moves* into the channel and back out to the receiver, so there's never a moment where two threads hold the same value - the data race is structurally impossible. The `for received in rx` loop pulls values as they arrive and ends cleanly when `tx` drops (closing the channel). No `Mutex`, no `.lock()`, no shared state to coordinate.

💡 **A rule of thumb worth keeping.** *Share by communicating where you can; reach for `Arc<Mutex>` when you must share state.* Message passing keeps each piece of data owned by exactly one thread at a time, sidestepping a whole class of locking headaches.

**One last horizon: `async`/`await`.** Everything above is about *threads* - great for CPU-bound work, where multiple cores chew on a problem at once. But for *I/O-bound* work - a server juggling thousands of network connections that spend most of their time waiting - spawning a thread per connection is wasteful. Rust's answer is `async`/`await`: functions that can *pause* while waiting for I/O and let the same thread do other work meanwhile, handling thousands of tasks on a handful of threads. It needs a runtime to drive it (the ecosystem standard is **Tokio**), and it's a substantial topic of its own. The same fearless-concurrency guarantees carry over: the compiler keeps async code data-race-free too.

## Recap

1. **`thread::spawn`** runs a closure on a new thread and returns a **`JoinHandle`**; call **`.join()`** to wait for it to finish (output order between threads is not guaranteed). Use a **`move`** closure to give the thread ownership of the data it uses.
2. **Data races can't compile.** Naive shared mutation fails the borrow checker - the same ownership rules from Phase 6 forbid two threads from freely mutating one value, so the bug class never ships.
3. **`Arc<Mutex<T>>`** is how you share mutable state safely: `Arc` for thread-safe shared ownership, `Mutex` for one-writer-at-a-time access via `.lock()`. ⚠️ It prevents data races, *not* deadlocks - those are logic bugs you must still avoid.
4. **`Send`** (safe to move to another thread) and **`Sync`** (safe to share by reference) are marker traits the compiler uses to enforce all of the above. Most types get them automatically; `Rc` is neither, which is exactly why `Arc` exists.
5. **Channels** (`std::sync::mpsc`) let threads communicate by passing values, moving ownership across the boundary - often cleaner than shared state. Reach for `async`/`await` (with a runtime like Tokio) when the bottleneck is I/O, not CPU.

## Quick check

Make sure the core idea stuck - that Rust's thread safety is just ownership, extended:

```quiz
[
  {
    "q": "Why does a `move` closure passed to `thread::spawn` need to take ownership of the data it uses?",
    "choices": [
      "Because the spawned thread may outlive the function that created the data, so borrowing it could leave a dangling reference",
      "Because `move` makes the closure run faster",
      "Because threads cannot read any data unless they own it, even temporarily",
      "Because `spawn` always copies its closure twice"
    ],
    "answer": 0,
    "explain": "A spawned thread can outlive the scope it was created in. If it only borrowed the data, that data could be dropped while the thread still used it - a dangling reference. `move` transfers ownership to the thread, so the data lives as long as the thread does."
  },
  {
    "q": "In `Arc<Mutex<T>>`, what does each of the two pieces do?",
    "choices": [
      "`Arc` provides thread-safe shared ownership; `Mutex` ensures only one thread accesses the data at a time",
      "`Arc` locks the data; `Mutex` counts references",
      "Both do the same thing, so either one alone is enough",
      "`Arc` prevents deadlocks; `Mutex` prevents memory leaks"
    ],
    "answer": 0,
    "explain": "`Arc` is an atomically reference-counted pointer that lets multiple threads co-own the value. `Mutex` is the lock that guarantees exclusive access - one writer at a time via `.lock()`. Stacked together, they share mutable state safely."
  },
  {
    "q": "Why can't you send an `Rc<T>` to another thread, while an `Arc<T>` is fine?",
    "choices": [
      "`Rc` isn't `Send` because its reference count isn't atomic and would race; `Arc` uses atomic counting and is `Send`",
      "`Rc` is too large to fit in a thread's stack",
      "`Arc` is just an older name for `Rc`, so there's no real difference",
      "`Rc` only works inside `async` functions"
    ],
    "answer": 0,
    "explain": "`Rc`'s reference count is a plain integer; two threads modifying it at once would corrupt the count, so `Rc` is not `Send` and the compiler rejects sending it across threads. `Arc` uses atomic operations for its count, making it safe to share - which is exactly why it exists."
  }
]
```


---

# Closures, Iterators & Zero-Cost Abstractions - Expressive and Fast

Back in [Phase 9](09-idioms-and-gotchas.md) you got a taste of `.iter().filter().map().collect()` - the chain that reads like a sentence. It looked clean, maybe a little magical. This phase turns the magic into a mental model: why those chains work, what the funny `|x| ...` syntax actually is, and the claim that makes the whole thing remarkable - that this high-level, readable code runs *exactly* as fast as the low-level loop you'd dread writing by hand.

That claim has a name - **zero-cost abstraction** - and it's close to the heart of Rust's pitch. Most languages make you choose: write expressive code that's slow, or fast code that's ugly. Rust says you shouldn't have to. By the end of this phase you'll understand the three pieces that make it true: closures, the function traits, and the iterator protocol.

## Closures - functions that remember where they came from

**What it actually is.** A closure is an anonymous function you can write inline - but with a superpower a plain function doesn't have: it can *capture* variables from the surrounding scope and carry them along. The syntax: pipes for the parameters, then the body - `|x| x + 1`.

📝 **Closure** - an anonymous function value that can capture (remember) variables from the environment where it was defined. "Closure" because it *closes over* the surrounding variables, keeping them alive inside the function.

```rust
fn main() {
    let add_one = |x: i32| x + 1;       // a closure, stored in a variable
    println!("{}", add_one(5));

    let offset = 100;                   // a variable in the surrounding scope
    let add_offset = |x: i32| x + offset; // the closure captures `offset`
    println!("{}", add_offset(5));
}
```
```console
$ cargo run
6
105
```
*What just happened:* `add_one` is a function with no name, written where it's used. `add_offset` is the interesting one - it mentions `offset`, a variable from outside the closure, and Rust quietly *captures* it so the value travels with the closure. A plain `fn` can't do that; it has no access to local variables around it.

By default a closure borrows what it captures. But sometimes you need it to *own* its captures - to hand the closure to another thread (Phase 14) or return it from a function. The `move` keyword forces capture *by value*:

```rust
fn main() {
    let name = String::from("Ada");

    // `move` takes ownership of `name` into the closure
    let greet = move || println!("Hello, {}", name);

    greet();
    // println!("{}", name);  // ERROR: `name` was moved into the closure
}
```
```console
$ cargo run
Hello, Ada
```
*What just happened:* Without `move`, the closure would borrow `name`. With `move`, it took ownership - `name` now lives *inside* `greet`, and using it afterward would be a compile error, exactly like the moves from [Phase 6](06-ownership-and-borrowing.md). Use `move` when the closure needs to outlive the scope it was born in.

## `Fn`, `FnMut`, `FnOnce` - how a closure treats its captures

Here's the question Rust must answer for every closure: when it uses a captured variable, does it just *read* it, *mutate* it, or *consume* it entirely? The answer determines how many times you can call the closure and where you're allowed to use it. Rust encodes it as three traits, and a closure automatically implements whichever fit.

📝 **`FnOnce`** - a closure that *consumes* its captures (e.g. moves a value out). It can be called **once**, because after that the captured values are gone. Every closure is at least `FnOnce`.

📝 **`FnMut`** - a closure that *mutably borrows* its captures, so it can change them. Callable many times, but it needs mutable access while it runs.

📝 **`Fn`** - a closure that only *immutably borrows* its captures (reads them). Callable many times, freely, even from multiple places at once.

These nest from most-restrictive to most-permissive: every `Fn` is also an `FnMut`, and every `FnMut` is also an `FnOnce`. **You almost never write these by hand** - the compiler picks the most permissive trait that fits your closure's body. Your job is mostly to know what they mean when they show up in signatures.

And they show up constantly, because a function that *accepts* a closure is generic over one of these traits. `impl Fn(i32) -> i32` means "give me anything callable like this, that only reads its captures":

```rust
// Accepts any closure that takes an i32 and returns an i32, reading its captures.
fn apply_twice(f: impl Fn(i32) -> i32, start: i32) -> i32 {
    f(f(start))   // called twice - so it must be Fn, not FnOnce
}

fn main() {
    let bump = 10;
    let result = apply_twice(|x| x + bump, 5);  // closure captures `bump` by read
    println!("{}", result);
}
```
```console
$ cargo run
25
```
*What just happened:* `apply_twice` calls `f` twice, so it demands `impl Fn` - a closure it can call repeatedly. We passed `|x| x + bump`, which only *reads* `bump`, so the compiler certifies it as `Fn` and the call typechecks. Had `apply_twice` asked for `impl FnOnce` instead, calling `f` a second time would fail to compile, since `FnOnce` only promises one call. This is how the borrow rules from [Phase 6](06-ownership-and-borrowing.md) extend to functions-as-values: the trait *is* the contract for how the closure touches its captures.

💡 **Key point.** Read `impl Fn(...)` as "a callable that reads its captures, usable many times." `FnMut` is "callable many times, but mutates." `FnOnce` is "callable exactly once." You rarely choose which one your closure is - the compiler does - but reading them tells you instantly how a function intends to use the closure you hand it.

## The `Iterator` trait - one method to rule them all

Now the centerpiece. Every loop, every `.map()`, every `for` you've written in Rust runs on a single, almost comically small trait.

📝 **`Iterator`** - a trait with one required method, `fn next(&mut self) -> Option<Self::Item>`. Call `next()` and it hands you `Some(value)` for the next item, or `None` when there's nothing left. *Everything else* - `map`, `filter`, `sum`, the works - is a default method built on `next`.

That tiny contract has a profound consequence: **iterators are lazy**. Calling `next` is the *only* thing that produces a value. Until something calls it, an iterator is an inert recipe.

You'll usually get iterators from collections (`.iter()`, `.into_iter()`), but implementing the trait yourself is the best way to see there's no magic. Here's a counter that yields the numbers below a limit:

```rust
struct Counter {
    count: u32,
    max: u32,
}

impl Iterator for Counter {
    type Item = u32;                    // what each item is

    fn next(&mut self) -> Option<u32> {
        if self.count < self.max {
            self.count += 1;
            Some(self.count)            // hand out the next value
        } else {
            None                        // signal "we're done"
        }
    }
}

fn main() {
    let counter = Counter { count: 0, max: 3 };
    for n in counter {                  // the `for` loop calls next() for us
        println!("{}", n);
    }
}
```
```console
$ cargo run
1
2
3
```
*What just happened:* We implemented exactly one method, `next`. Each call bumps `count`, returns `Some(count)`, and returns `None` once we hit `max`. The `for` loop does nothing fancier than calling `next()` over and over and stopping the instant it sees `None` - the same protocol the Python guide describes for [`StopIteration`](/guides/python-from-zero), only here the "we're done" signal is `Option`'s `None`. Define `next`, and your type drops straight into every `for` loop and adapter in the standard library.

## Iterator adapters - building pipelines, lazily

Because everything is built on `next`, the standard library offers a huge toolbox of methods that take an iterator and return a *new* iterator. These are **adapters**, and they're lazy - they wrap your iterator in another layer of "recipe" without running anything. The methods that actually drive the iterator and produce a final value are **consumers**, and they're eager.

A useful way to hold the two apart:

- **Adapters (lazy, return an iterator):** `map` (transform each item), `filter` (keep some items), `take` (stop after N), `zip` (pair two iterators together), `enumerate` (attach an index to each item).
- **Consumers (eager, return a value):** `collect` (gather into a collection), `sum` (add them up), `fold` (reduce with an accumulator), `for_each` (run a side effect per item).

The pattern is always: chain adapters to describe the transformation, then end with one consumer to make it happen.

```rust
fn main() {
    let names = ["alice", "bob", "carol", "dave"];

    let result: Vec<String> = names
        .iter()
        .enumerate()                          // (0, "alice"), (1, "bob"), ...
        .filter(|(i, _)| i % 2 == 0)          // keep even indices
        .map(|(i, name)| format!("{}: {}", i, name))
        .take(5)                              // at most 5 (we have fewer)
        .collect();                           // <-- the consumer fires it all

    for line in &result {
        println!("{}", line);
    }

    let total: u32 = (1..=5).sum();           // a one-shot consumer
    println!("sum 1..=5 = {}", total);
}
```
```console
$ cargo run
0: alice
2: carol
sum 1..=5 = 15
```
*What just happened:* Read the chain top to bottom as a pipeline: number each name with `enumerate`, keep the even-indexed ones with `filter`, format each survivor with `map`, cap the count with `take`, then `collect` pulls every value through and builds the `Vec`. None of `enumerate`/`filter`/`map`/`take` did any work when written; they only described layers. `collect()` is what called `next` enough times to run the whole thing. The `(1..=5).sum()` line shows a range is also an iterator, consumed in one shot.

⚠️ **Adapters do nothing until a consumer runs.** This is the single most common iterator mistake. Write `names.iter().map(|n| println!("{}", n));` with no consumer, and *nothing prints* - you built a recipe and threw it away. The compiler even warns you: `iterators are lazy and do nothing unless consumed`.

## Zero-cost abstractions - readable *and* fast, not one or the other

So we have closures and a deeply layered iterator system. In most languages, layering like that costs you: each adapter would be an object with virtual method calls, heap allocations, the works - and the pretty pipeline would run measurably slower than a blunt `for` loop. Rust's twist: **it doesn't.**

📝 **Zero-cost abstraction** - a high-level construct that compiles down to the same machine code you'd have written by hand at the low level, with no runtime penalty for the abstraction. You don't pay for the niceness.

Three things from earlier phases combine to make this real. **Monomorphization** ([Phase 11's generics](11-traits-and-generics.md)): because `apply_twice` and every adapter are generic, the compiler stamps out a specialized version for your exact closure and type - no dynamic dispatch. **Inlining:** small closures and `next` calls get inlined directly into the loop, collapsing the layers of "recipe" into flat code. And the **ownership model** ([Phase 6](06-ownership-and-borrowing.md)) often lets the compiler prove indices are in bounds, eliding the array bounds-checks a naive loop might keep.

The upshot: this pipeline...

```rust
fn main() {
    let nums: Vec<u64> = (1..=1_000).collect();

    // High-level: reads like a sentence.
    let sum_of_even_squares: u64 = nums
        .iter()
        .filter(|&&n| n % 2 == 0)
        .map(|&n| n * n)
        .sum();

    println!("{}", sum_of_even_squares);
}
```

...compiles to essentially the same instructions as this hand-rolled version:

```rust
fn main() {
    let nums: Vec<u64> = (1..=1_000).collect();

    // Low-level: the loop you'd write to avoid "overhead."
    let mut sum_of_even_squares: u64 = 0;
    for &n in &nums {
        if n % 2 == 0 {
            sum_of_even_squares += n * n;
        }
    }

    println!("{}", sum_of_even_squares);
}
```
```console
$ cargo run
167167000
```
*What just happened:* Both versions produce `167167000`, and after the optimizer runs, both produce nearly identical machine code - no extra allocations, no closure objects on the heap, no virtual calls. The `filter` and `map` closures were inlined into one tight loop. The choice between them is purely about *which you'd rather read and maintain* - the performance is the same.

💡 **You don't trade readability for speed.** That's the whole pitch of this phase, and arguably of Rust. In many languages the iterator chain is the "elegant but slower" option you avoid in hot loops. In Rust it's the idiomatic default precisely *because* it costs nothing. Reach for the chain first; drop to a manual loop only when profiling gives you a concrete reason, which is rare.

## Recap

1. A **closure** is an anonymous function written inline (`|x| x + 1`) that can **capture** variables from its surrounding scope. By default it borrows them; `move` makes it take ownership - needed for threads or returning the closure.
2. **`Fn` / `FnMut` / `FnOnce`** describe how a closure treats its captures: `Fn` reads (callable many times), `FnMut` mutates (callable many times), `FnOnce` consumes (callable once). The compiler picks the right one; a function accepting a closure is generic over these traits, e.g. `impl Fn(i32) -> i32`.
3. The **`Iterator`** trait requires one method, `next() -> Option<Item>`. Everything else is built on it, and that means iterators are **lazy** - no value exists until something calls `next`.
4. **Adapters** (`map`, `filter`, `take`, `zip`, `enumerate`) are lazy and return iterators; **consumers** (`collect`, `sum`, `fold`, `for_each`) are eager and produce a value. ⚠️ Adapters do nothing until a consumer runs - forget the consumer and you get the "unused iterator" warning and no output.
5. **Zero-cost abstraction**: thanks to monomorphization, inlining, and the borrow checker, an iterator chain compiles to the same machine code as a hand-written loop. You get readable *and* fast - you don't trade one for the other.

## Quick check

Test yourself on the three ideas that make this phase tick - capturing, laziness, and zero cost:

```quiz
[
  {
    "q": "What distinguishes a closure like `|x| x + offset` from a plain `fn`?",
    "choices": [
      "It can capture variables from the surrounding scope (like `offset`), carrying them along",
      "It always runs faster than a named function",
      "It can only ever be called once",
      "It cannot take any parameters"
    ],
    "answer": 0,
    "explain": "A closure closes over its environment - it can capture and remember variables from where it was defined, which a plain `fn` cannot. By default it borrows them; `move` makes it take ownership."
  },
  {
    "q": "You write `names.iter().map(|n| println!(\"{}\", n));` and nothing prints. Why?",
    "choices": [
      "`map` is a lazy adapter - without a consumer like `collect`, `for_each`, or a `for` loop, nothing pulls the values through",
      "`println!` doesn't work inside a closure",
      "`.iter()` returns an empty iterator for arrays",
      "The closure needs the `move` keyword to run"
    ],
    "answer": 0,
    "explain": "Iterator adapters are lazy: they build a recipe but do no work until a consumer calls `next`. With no consumer, the chain is dropped unused - the compiler even warns 'iterators are lazy and do nothing unless consumed.'"
  },
  {
    "q": "Why does an idiomatic `.iter().filter(...).map(...).sum()` chain run as fast as a hand-written `for` loop in Rust?",
    "choices": [
      "Monomorphization and inlining collapse the adapters and closures into the same machine code as the manual loop - a zero-cost abstraction",
      "The chain secretly skips most of the elements to save time",
      "Rust runs iterator chains on a separate optimized thread",
      "It doesn't - the chain is always noticeably slower, so you should avoid it"
    ],
    "answer": 0,
    "explain": "The generic adapters get specialized (monomorphized) for your exact types and the small closures get inlined, so the layers collapse into one tight loop with no heap allocations or virtual calls. Readable and fast - that's the zero-cost promise."
  }
]
```


---

# Macros & Metaprogramming - Code That Writes Code

You've been using macros since Phase 1, every time you typed `println!`. That `!` has probably sat there as a small unexplained mystery - punctuation you copy without knowing why. This phase clears it up, and the answer turns out to be one of the more elegant ideas in Rust.

Macros intimidate people, with a reputation for arcane wizard stuff. The truth is gentler: a macro is a tool that writes ordinary Rust code *for* you, before your program is compiled. Once you have that model, the mystery dissolves - and you'll realize you'll spend far more time *using* macros than ever writing them.

📝 **Macro** - a piece of code that runs at **compile time** and expands into ordinary Rust source. By the time the compiler actually compiles your program, every macro call has been replaced by the plain code it generated. A macro is not a function; it's a code generator.

## Why the `!`

Every `!` you've typed marks a macro call, not a function call: `println!`, `vec!`, `panic!`, `format!`, `assert!`. The bang is Rust's way of saying "this isn't a function - it expands into code before compilation." That distinction is the whole point of this phase.

Why can't these be plain functions? Two reasons, and `println!` shows both:

```rust
fn main() {
    let name = "Ada";
    let count = 3;

    println!("hello");                          // zero extra args
    println!("hello {}", name);                 // one
    println!("{} sent {} messages", name, count); // two
}
```
```console
$ cargo run
hello
hello Ada
Ada sent 3 messages
```
*What just happened:* The same `println!` accepted zero, one, and two trailing arguments. A normal Rust function has a *fixed* number of parameters with *fixed* types - there's no way to write one `fn` that takes "a format string plus however many values you feel like." A macro can, because instead of a signature it has *patterns* it matches against whatever you hand it.

The more impressive reason: `println!` checks your format string **at compile time**. Forget an argument and the program won't even build:

```rust
fn main() {
    println!("{} and {}", "only one");  // string has two {} but one value
}
```
```console
$ cargo build
error: 2 positional arguments in format string, but there is 1 argument
 --> src/main.rs:2:15
```
*What just happened:* The macro read your literal `"{} and {}"` *during compilation*, counted two placeholders, saw one value, and refused to build. A regular function receives its arguments at runtime and can't see inside a string literal like that. Because a macro runs at compile time with your source code in hand, it catches the mistake before your program ever runs - the superpower the `!` is announcing.

## Declarative macros (`macro_rules!`)

The most common kind of macro you can write yourself is the **declarative macro**, built with `macro_rules!`. The mental model: a tiny pattern-matching engine, a lot like `match` from [Phase 9](09-idioms-and-gotchas.md) - except instead of matching on *values*, it matches on the *shape of code* you pass it, and instead of returning a value, it produces *new code*.

A small example: `max!` takes two expressions and expands into an `if` that picks the larger:

```rust
macro_rules! max {
    ($a:expr, $b:expr) => {
        if $a > $b { $a } else { $b }
    };
}

fn main() {
    let biggest = max!(3 + 1, 10);
    println!("{}", biggest);
}
```
```console
$ cargo run
10
```
*What just happened:* Read the macro as a rule. The left side, `($a:expr, $b:expr)`, is the **matcher** - "I expect two things, each an expression; call them `$a` and `$b`." `:expr` is a *fragment specifier* telling Rust what kind of code to capture. The right side, between `=>` and `;`, is the **expansion** - the code to generate, with `$a` and `$b` slotted in. So `max!(3 + 1, 10)` expanded, at compile time, into `if 3 + 1 > 10 { 3 + 1 } else { 10 }`, and the compiler compiled *that* ordinary code.

Now the feature that makes macros genuinely powerful: **repetition**. A matcher can say "zero or more of these," which is how `vec!` accepts any number of elements. Let's build our own:

```rust
macro_rules! my_vec {
    ( $( $x:expr ),* ) => {
        {
            let mut v = Vec::new();
            $( v.push($x); )*
            v
        }
    };
}

fn main() {
    let nums = my_vec![10, 20, 30];
    println!("{:?}", nums);
}
```
```console
$ cargo run
[10, 20, 30]
```
*What just happened:* The matcher `$( $x:expr ),*` reads as "a comma-separated list of expressions" - `$( ... )` wraps the repeating part, `,` is the separator, `*` means "zero or more." For `my_vec![10, 20, 30]`, that captured `$x` three times. In the expansion, `$( v.push($x); )*` repeats the `v.push($x);` line once per captured expression, so the macro generated a fresh `Vec`, pushed `10`, `20`, and `30`, and handed it back - essentially how the real `vec!` works.

💡 **Key point.** A `macro_rules!` matcher captures *fragments of code* (`$x:expr`) and the expansion stamps them into a template, optionally repeating with `$( ... )*`. Because this all happens before compilation, the generated code is just as fast as if you'd written it by hand - zero runtime cost.

## When a declarative macro earns its keep

A fair question after writing `max!`: why not a function? For `max!`, you absolutely should use a function (or `std::cmp::max`) - it'd be clearer. Macros earn their keep only when ordinary tools *can't* do the job. The classic case is generating genuinely repetitive code that functions and generics can't express - most often, implementing the same trait across many types:

```rust
trait Describe {
    fn describe(&self) -> String;
}

macro_rules! impl_describe {
    ($($t:ty),*) => {
        $(
            impl Describe for $t {
                fn describe(&self) -> String {
                    format!("a {} with value {}", stringify!($t), self)
                }
            }
        )*
    };
}

impl_describe!(i32, f64, bool);

fn main() {
    println!("{}", 42.describe());
    println!("{}", 3.5.describe());
    println!("{}", true.describe());
}
```
```console
$ cargo run
a i32 with value 42
a f64 with value 3.5
a bool with value true
```
*What just happened:* `impl_describe!(i32, f64, bool)` matched a comma-separated list of *types* (`$t:ty`), and `$( ... )*` stamped out three separate `impl Describe for ...` blocks - one per type - at compile time. Writing those by hand would be tedious and easy to get out of sync; the macro keeps them in lockstep. (`stringify!` is itself a macro that turns the token `i32` into the string `"i32"`.) This is the sweet spot: boilerplate that varies only by type, which a plain generic function can't generate because each `impl` is a separate language construct.

⚠️ **Gotcha - macros are harder to read and debug than functions.** That `max!` evaluates `$a` and `$b` *twice* in the expansion, so `max!(expensive(), 0)` would call `expensive()` twice - a subtle bug a function would never have. Macro errors point at the *expanded* code, not your source, which can be baffling. Reach for a macro only when ordinary code genuinely can't do the job; when in doubt, write the function.

## Derive macros

Here's the macro feature you'll use constantly - and you've already met it. Remember `#[derive(Debug)]` from way back? That's a macro - specifically a **procedural macro**: a code generator the compiler runs on your type to write `impl` blocks *for* you.

📝 **Declarative vs procedural.** A **declarative** macro (`macro_rules!`) is pattern-based - you write matchers and templates, as above. A **procedural** macro is a small Rust program that receives your code as input and *computes* the output code with ordinary Rust logic. `#[derive(...)]` is the most common procedural macro.

```rust
#[derive(Debug, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let a = Point { x: 1, y: 2 };
    let b = a.clone();              // Clone gave us .clone()
    println!("{:?}", a);           // Debug gave us {:?}
    println!("equal? {}", a == b); // PartialEq gave us ==
}
```
```console
$ cargo run
Point { x: 1, y: 2 }
equal? true
```
*What just happened:* That one line, `#[derive(Debug, Clone, PartialEq)]`, ran three procedural macros at compile time, each generating a full `impl` block for `Point`: `Debug` wrote the code that prints `Point { x: 1, y: 2 }`, `Clone` wrote the field-by-field `.clone()`, and `PartialEq` wrote the `==` comparison. You'd have written dozens of lines by hand; `derive` generated them from the struct definition alone.

## Procedural macros, briefly

Custom `#[derive(...)]` is one of three kinds of procedural macro. You don't need to write any of these to be productive, but you'll meet them constantly in libraries:

- **Custom derive** - `#[derive(Serialize)]` from the `serde` crate generates JSON (de)serialization code for your struct. This is how serialization in Rust feels effortless.
- **Attribute macros** - `#[tokio::main]` on your `main` function rewrites it to set up an async runtime, and a web framework's `#[rocket::get("/")]` turns a plain function into a route handler. They wrap or transform the item they're attached to.
- **Function-like macros** - they look like `macro_rules!` calls (`name!(...)`) but are backed by a full Rust program; `sqlx::query!` checks your SQL against a real database at compile time.

All three operate on the **token stream** - the raw sequence of tokens making up your code - using `proc_macro` machinery. They're powerful enough to inspect and rewrite arbitrary code, which is why they must live in their own dedicated crate. Writing one is an advanced topic involving crates like `syn` and `quote`, a deep dive we won't take here.

💡 **You'll *use* far more macros than you *write*.** The macros powering `serde`, `tokio`, `clap`, and `sqlx` represent enormous engineering effort, and you get all of it from a single `#[derive(...)]` or `#[tokio::main]` line. For the vast majority of Rust you'll write, "knowing macros" means knowing *which* ones to reach for, not authoring your own. That's a feature, not a gap.

## Recap

1. Every `!` (`println!`, `vec!`, `panic!`) marks a **macro** call. Macros run at **compile time** and expand into ordinary Rust code before your program is compiled - that's why they can take any number of arguments and check format strings before the program runs.
2. **Declarative macros** (`macro_rules!`) pattern-match on the *shape of code*: matchers capture fragments like `$x:expr` or `$t:ty`, and `$( ... )*` repeats the expansion once per captured item - exactly how `vec!` accepts any number of elements.
3. A declarative macro **earns its keep** only when functions and generics can't, such as implementing one trait across many types. ⚠️ Macros are harder to read and debug, so prefer a plain function when one will do.
4. **Derive macros** like `#[derive(Debug, Clone, PartialEq)]` are **procedural** macros that generate whole `impl` blocks from your type - the macro feature you'll use on nearly every struct.
5. **Procedural macros** come in three flavors (custom derive, attribute, function-like), operate on the token stream, live in their own crate, and power libraries like `serde` and `tokio`. You'll *use* them far more than you'll ever *write* them.

## Quick check

One quick pass to lock in the core idea - that a macro is compile-time code generation:

```quiz
[
  {
    "q": "Why does `println!` have a `!`, and why can't it be a plain function?",
    "choices": [
      "The `!` marks it as a macro that expands at compile time, letting it take any number of arguments and check the format string before the program runs",
      "The `!` means the function can panic, which regular functions are forbidden from doing",
      "The `!` is just Rust's required syntax for any function that prints to the screen",
      "The `!` makes the call faster by skipping argument type checks"
    ],
    "answer": 0,
    "explain": "The `!` marks a macro call. Because a macro expands into code at compile time rather than being called at runtime, it can accept a variable number of arguments and inspect the format-string literal to catch mistakes before the program ever runs - neither of which a fixed-signature function can do."
  },
  {
    "q": "In `macro_rules! my_vec { ( $( $x:expr ),* ) => { ... } }`, what does `$( $x:expr ),*` match?",
    "choices": [
      "A comma-separated list of zero or more expressions, captured as `$x`",
      "Exactly one expression named `$x`",
      "A single string literal split on commas",
      "Two expressions separated by a comma, no more and no fewer"
    ],
    "answer": 0,
    "explain": "The `$( ... ),*` is a repetition: `$( )` wraps the repeating part, `,` is the separator, and `*` means zero or more. So it captures a comma-separated list of expressions, each bound to `$x`, which the expansion then stamps out one at a time."
  },
  {
    "q": "What is `#[derive(Debug, Clone, PartialEq)]` actually doing to your struct?",
    "choices": [
      "Running procedural macros at compile time that generate full `impl` blocks (for `{:?}`, `.clone()`, and `==`) from the struct definition",
      "Importing three traits from the standard library at runtime",
      "Marking the struct as one whose fields can never change",
      "Telling the compiler to skip type-checking those three traits"
    ],
    "answer": 0,
    "explain": "`derive` invokes procedural macros that read your struct and generate the corresponding trait `impl` blocks at compile time - `Debug` produces the `{:?}` formatting code, `Clone` the `.clone()` method, and `PartialEq` the `==` comparison. It's the everyday face of metaprogramming in Rust."
  }
]
```


---

# Performance, Unsafe & the Ecosystem - The Last Mile

You've come a long way. You can model data with enums, wrangle the borrow checker, write your own traits and macros. This phase is the last mile of the deep half - the practical knowledge that separates "I can write Rust" from "I can ship Rust." We'll cover why your code is probably already fast, the one switch that makes it *dramatically* faster, how to measure instead of guess, what the scary-sounding `unsafe` keyword actually means, and the handful of crates you'll lean on every day.

The throughline: Rust gives you control without making you pay for ceremony you don't use.

## Rust is fast by default

**What's actually going on.** Rust has no garbage collector pausing your program to clean up, and no runtime interpreting your code - it compiles straight to native machine code, like C and C++. On top of that, the abstractions you've used all guide are **zero-cost** (from [Phase 15](15-closures-and-iterators.md)): an iterator chain, a `match`, an `Option` - they compile down to the same instructions you'd write by hand, with nothing extra at runtime.

```rust
fn main() {
    let nums = [1, 2, 3, 4, 5, 6];

    // Reads like a high-level pipeline...
    let total: i32 = nums.iter().filter(|&&n| n % 2 == 0).map(|&n| n * n).sum();

    println!("{}", total); // 4 + 16 + 36
}
```
```console
$ cargo run
56
```
*What just happened:* That `.iter().filter().map().sum()` chain looks like it allocates intermediate collections and walks the data several times. It doesn't - the compiler fuses the whole thing into a single tight loop with no heap allocation, byte-for-byte what a hand-written `for` loop would produce. You get the readable version *and* the fast version at once.

💡 **Key insight.** Most Rust is fast without you trying. The ownership system, the lack of a GC, and zero-cost abstractions mean idiomatic code is usually already efficient. Your job is rarely "make this faster" from scratch - it's "don't accidentally make it slow" and "find the one spot that actually matters." Which brings us to the switch everyone forgets.

## The one switch everyone forgets: `--release`

This is the single most common Rust performance mistake, and it bites beginners and experienced developers alike. By default, `cargo run` and `cargo build` produce a **debug build**, tuned for fast *compilation* and good *debugging*, not fast *execution*: optimizations are off, and extra runtime checks (like the integer-overflow panic from [Phase 9](09-idioms-and-gotchas.md)) are on.

⚠️ **Gotcha - never benchmark or ship a debug build.** A debug build can be *ten to a hundred times slower* than a release build for compute-heavy code. People regularly conclude "Rust is slow" after timing a debug binary. The fix is one flag.

```console
$ cargo run                 # debug: slow, with overflow checks
$ cargo run --release       # optimized: this is the real speed
$ cargo build --release     # binary lands in target/release/
```

To make the gap concrete, imagine timing a number-crunching loop both ways:

```console
$ cargo run --quiet -- crunch
debug build:    8.42s

$ cargo run --release --quiet -- crunch
release build:  0.11s
```
*What just happened:* The exact same code, the same input - the only difference is `--release`. The optimizer inlined functions, unrolled loops, and dropped the debug-only checks, turning eight seconds into a tenth of one. (These numbers are illustrative; the actual factor depends on your machine and code. The lesson - *debug is for developing, release is for measuring and shipping* - holds everywhere.)

Develop and test with plain `cargo`, but the moment you care about speed - benchmarking, profiling, or handing a binary to a user - reach for `--release`.

## Measure, then optimize

Once you're on a release build, the next rule is older than Rust: **don't guess where the time goes - measure.** Programmers are famously bad at predicting hotspots. You'll spend an afternoon shaving nanoseconds off a function that runs twice, while the real cost hides in a loop you never suspected.

The biggest wins almost never come from micro-tweaks. They come from **algorithmic cost** - the difference between an approach that scales gracefully and one that falls off a cliff as data grows. The classic example: scanning a list to look things up (`O(n)` per lookup, `O(n²)` in a loop) versus a `HashMap` (`O(1)` per lookup).

```rust
use std::collections::HashMap;

fn main() {
    let prices = vec![("apple", 3), ("pear", 5), ("plum", 2)];
    let orders = ["pear", "apple", "pear", "plum", "apple"];

    // Slow path: for each order, scan the whole list to find its price → O(n*m).
    // Fast path: build a HashMap once, then every lookup is ~O(1).
    let lookup: HashMap<&str, i32> = prices.into_iter().collect();

    let total: i32 = orders.iter().map(|name| lookup[name]).sum();
    println!("{}", total); // 5 + 3 + 5 + 2 + 3
}
```
```console
$ cargo run --release
18
```
*What just happened:* We paid a one-time cost to build a `HashMap` from the price list, and every lookup became near-instant regardless of how many products exist. With the scan-the-list approach, doubling the product count *and* order count roughly quadruples the work; with the `HashMap`, it barely moves. No amount of micro-optimizing the slow version would ever catch up - the algorithm dominates.

If big-O notation feels fuzzy, this is worth internalizing before you tune anything: [the cost of an algorithm, without the math panic](/guides/big-o-without-the-math-panic). Play with how different growth rates diverge as input scales:

```playground-bigo
```

**Once the algorithm is sound, *then* reach for the profilers.** Rust has excellent tooling for finding real hotspots:

- **`criterion`** - a benchmarking crate that runs your code many times, accounts for noise, and gives statistically trustworthy numbers (far better than a hand-rolled timer).
- **`cargo flamegraph`** - generates a flame graph showing which functions eat the most time across a whole run. The widest bars are where to look.
- **`perf`** (Linux) - the low-level system profiler `cargo flamegraph` builds on; reach for it for fine-grained CPU data.

💡 **Key insight.** Profile *before* you optimize, and again *after*. The measurement tells you where to spend effort and proves your change helped. Optimizing without measuring is how you make code uglier and no faster.

## `unsafe` - what it really is

The word `unsafe` scares people away, and the fear is mostly a misunderstanding.

📝 **`unsafe`** - a keyword that unlocks five specific abilities the compiler can't verify for you. It does **not** turn off the borrow checker, and does **not** mean "dangerous code lives here." Inside an `unsafe` block, ownership, borrowing, and lifetime rules all still apply exactly as before. What changes is that *you* take responsibility for upholding a handful of invariants the compiler normally checks - because in these specific cases, it can't.

The five superpowers `unsafe` grants, and nothing more:

1. Dereference a **raw pointer** (`*const T` / `*mut T`).
2. Call a function marked `unsafe` (including foreign C functions via FFI).
3. Access or modify a **mutable `static`** variable.
4. Implement an `unsafe` trait.
5. Access the fields of a **`union`**.

**Why it exists.** Some things are genuinely safe but impossible for the compiler to *prove* safe. Talking to a C library (FFI) means calling code the borrow checker can't see. A high-performance data structure - a custom allocator, a lock-free queue - sometimes needs raw pointers. `unsafe` is the escape hatch for "I know this is correct; trust me and let me do it."

Here's the canonical tiny example - dereferencing a raw pointer:

```rust
fn main() {
    let x = 42;
    let ptr = &x as *const i32; // make a raw pointer (this part is safe)

    // Dereferencing it requires unsafe: the compiler can't guarantee
    // the pointer is still valid, so YOU promise that it is.
    let value = unsafe { *ptr };

    println!("{}", value);
}
```
```console
$ cargo run
42
```
*What just happened:* Creating the raw pointer `ptr` is allowed in safe code - it's just an address. *Reading through it* with `*ptr` needs `unsafe`, because a raw pointer carries no lifetime, so the compiler can't prove it still points at valid memory. Wrapping the deref in `unsafe` is you signing off: "I've verified `x` is alive and this address is good."

⚠️ **Gotcha - keep `unsafe` tiny and wrap it.** The discipline that makes `unsafe` manageable: make the block as small as possible, uphold the invariants right there, and expose a **safe** function around it so callers never touch `unsafe` themselves. This is exactly how the standard library works - `Vec`, `HashMap`, and friends use `unsafe` internally but present a fully safe API. Most application code never writes `unsafe` at all; it's a tool for library authors and FFI, not a daily driver.

## The ecosystem: crates worth knowing by name

Rust's standard library is deliberately small - it gives you the language essentials and leaves the rest to **crates.io**, the central package registry you pull from with `cargo add` (from [Phase 8](08-ecosystem-and-tooling.md)). The ecosystem is one of Rust's real strengths, and a handful of crates show up in nearly every serious project:

| Crate | What it does | When you reach for it |
|---|---|---|
| **`serde`** | Serialization framework | Converting structs to/from JSON, TOML, etc. The backbone of almost all Rust data handling. |
| **`tokio`** | Async runtime | Anything network- or IO-heavy: servers, clients, concurrent tasks. The de facto async standard. |
| **`rayon`** | Data parallelism | Turn a sequential iterator parallel by changing `.iter()` to `.par_iter()`. Effortless multi-core. |
| **`clap`** | Command-line arg parsing | Building any CLI tool - flags, subcommands, help text, all derived from a struct. |
| **`reqwest`** | HTTP client | Making HTTP requests (calling an API, fetching a URL) without hand-rolling sockets. |
| **`anyhow` / `thiserror`** | Error handling | The error ergonomics from [Phase 13](13-error-handling-deep.md): `anyhow` for applications, `thiserror` for libraries. |

💡 **Key insight.** Before writing your own serializer, argument parser, or HTTP client, check crates.io - there's almost certainly a well-maintained, battle-tested crate that does it better than a from-scratch version. Look for recent updates, lots of downloads, and good docs (every crate's docs live at `docs.rs`). Standing on the ecosystem's shoulders is idiomatic Rust, not a shortcut.

And with that, the deep half closes. You now understand Rust from `cargo run` down to `unsafe` - the type system, ownership, traits, generics, error handling, async, macros, and now performance and the ecosystem. What's left is knowing where to point it.

## Recap

1. **Rust is fast by default** - no GC, no runtime, native code, and zero-cost abstractions mean idiomatic code is usually already efficient. Your job is mostly to *not* make it slow.
2. **Always use `--release` for real speed.** Debug builds are far slower (no optimization, extra checks). Never benchmark or ship a debug binary - `cargo run --release` / `cargo build --release`.
3. **Measure, then optimize.** Algorithmic cost (an `O(1)` `HashMap` vs an `O(n²)` scan) dominates micro-tweaks. Use `criterion` to benchmark, `cargo flamegraph`/`perf` to find hotspots, and profile before *and* after.
4. **`unsafe` is narrow, not scary.** It doesn't disable the borrow checker - it unlocks five specific powers (raw-pointer deref, unsafe fn calls, mutable statics, unsafe traits, union fields) where you uphold invariants the compiler can't. Keep blocks tiny and wrap them in safe APIs.
5. **Lean on the ecosystem.** `serde`, `tokio`, `rayon`, `clap`, `reqwest`, `anyhow`/`thiserror` are the daily-drivers. Reach for a well-maintained crate before rolling your own.

## Quick check

Three questions on the ideas most likely to trip you in real work:

```quiz
[
  {
    "q": "You benchmark a Rust function and it seems painfully slow. What's the first thing to check?",
    "choices": [
      "Whether you built with `--release` - debug builds skip optimization and can be 10–100x slower",
      "Whether you need to rewrite the function in unsafe Rust for speed",
      "Whether Rust is the wrong language for the task",
      "Whether you should add more `.clone()` calls to help the compiler"
    ],
    "answer": 0,
    "explain": "The most common Rust performance mistake is timing a debug build. Debug builds turn off optimizations and add runtime checks. Always benchmark and ship with `cargo run --release` / `cargo build --release` before drawing any conclusions about speed."
  },
  {
    "q": "Which statement about `unsafe` is correct?",
    "choices": [
      "It unlocks five specific abilities (like dereferencing raw pointers) where you uphold invariants the compiler can't verify - the borrow checker still applies",
      "It completely turns off the borrow checker, so ownership rules no longer apply inside the block",
      "It means the code is dangerous and should be avoided in all projects",
      "It makes any code run faster automatically by skipping safety checks"
    ],
    "answer": 0,
    "explain": "`unsafe` does not disable the borrow checker. It grants exactly five superpowers (raw-pointer deref, calling unsafe fns, mutable statics, unsafe traits, union fields) where the compiler can't prove safety, so you take responsibility. Keep blocks tiny and wrap them in safe APIs."
  },
  {
    "q": "You're looking up values from a list thousands of times in a loop and it's slow. What's the highest-impact fix?",
    "choices": [
      "Switch from scanning the list (O(n) per lookup) to a HashMap (≈O(1) per lookup) - fix the algorithm",
      "Manually unroll the loop to save a few instructions per iteration",
      "Add an unsafe block around the lookup to skip bounds checks",
      "Convert the loop to use shorter variable names so it compiles faster"
    ],
    "answer": 0,
    "explain": "Algorithmic cost dominates micro-optimizations. Repeatedly scanning a list is O(n) per lookup (O(n²) overall); a HashMap makes each lookup ≈O(1). Changing the data structure beats any amount of low-level tweaking on the slow version. Measure first, then fix the algorithm."
  }
]
```


---

# Where to Go Next

If you've made it through ownership, errors, the tooling, and the idioms, take a second to notice what happened: you learned the hard part. The thing people are scared of - the borrow checker - you've now met face to face, and you understand what it's doing and why. Everything from here is *applying* what you know to a domain you care about. This phase is a short, clear map of where Rust shines and what to read next.

## Pick a direction by what you want to build

Rust isn't a "web language" or a "systems language" - it's genuinely good across a wide range, and the fastest way to get fluent is to build something real in an area that excites you. Here's the straight lay of the land:

```mermaid
flowchart TD
  R(You know Rust basics)
  R --> CLI[Command-line tools]
  R --> WEB[Web backends]
  R --> SYS[Systems & embedded]
  R --> WASM[WebAssembly]
  CLI -.-> clap[clap]
  WEB -.-> axum[axum / actix-web]
  WASM -.-> wb[wasm-bindgen]
```

- **Command-line tools.** Rust's sweet spot for a first real project - fast, single-binary, easy to share. Reach for **`clap`** for a polished CLI with help text and flags in an afternoon.
- **Web backends.** Rust makes fast, reliable servers. The two crates you'll hear about are **`axum`** and **`actix-web`**, both mature and widely used. *(The Missing Manual's own backend is a Rust server built with `axum`.)*
- **Systems & embedded.** The classic Rust territory - operating systems, databases, drivers, game engines, and microcontrollers with no operating system at all. Here "memory safety with no garbage collector" from [Phase 6](06-ownership-and-borrowing.md) pays its biggest dividends.
- **WebAssembly (WASM).** Compile Rust to run *in the browser* at near-native speed, often alongside JavaScript, via **`wasm-bindgen`**. Great for performance-critical web code like image processing, games, simulations.

You don't have to choose forever - pick the one that makes you want to open your editor tonight.

## The deep dive: *The Rust Programming Language*

When you want to go from "I can read and write Rust" to "I understand it thoroughly," there's one canonical resource: **"The Rust Programming Language,"** universally known as **"the Book."** It's the official, free, online book maintained by the Rust team - thorough, beginner-respecting, and the reference the community points to. (Find it at [doc.rust-lang.org/book](https://doc.rust-lang.org/book/).)

This guide gave you the working mental model fast; the Book gives you the complete picture at a comfortable pace. They pair well - come back to whatever chapter you're stuck on, and it'll go deeper than we had room to.

💡 **Key point.** The best next step isn't more reading - it's building. Pick a tiny project (a CLI that renames files, a web endpoint that returns the time, a number-cruncher) and finish it. You'll learn more from one completed small thing than three chapters you only read.

## A word about the borrow checker, before you go

Here's the plain, encouraging truth to carry with you: **the borrow checker stops fighting you sooner than you think.** Week one, it feels relentless. By the time you've built a project or two, you're writing code it accepts on the first try without consciously trying, because you've absorbed how it thinks. That frustration you may have felt in [Phase 6](06-ownership-and-borrowing.md) is temporary and *productive* - the feeling of learning to write code that doesn't have whole categories of bugs.

That's the deal Rust offers, plainly: more thinking up front, in exchange for programs that are fast and don't crash from memory bugs or data races. Every language makes a trade like this - if you're curious *why* they differ so much in these choices, [Programming Languages, Explained Like a Human](/guides/languages-explained-like-a-human) lays out the whole landscape and where Rust sits in it.

You've got the mental model now. Go build something. Welcome to Rust.

## Recap

1. **Choose by what you want to build:** CLIs (`clap`), web backends (`axum`/`actix-web`), systems & embedded, or WebAssembly (`wasm-bindgen`).
2. **The Book** ([doc.rust-lang.org/book](https://doc.rust-lang.org/book/)) is the official, free, thorough deep dive - pair it with hands-on building.
3. **Build a small thing and finish it** - that teaches more than more reading.
4. The **borrow checker stops fighting you sooner than you think**; the up-front thinking buys you fast, crash-resistant programs.
