Part I — Foundations: the Rust way
Part I covers the language you need to read any Rust program. Chapters 1–5 move quickly if you know Go or C. Chapters 6–8 (ownership, borrowing, slices) are where Rust differs from every language you know. Read those slowly, run every snippet, and break them on purpose. Part I ends with a checkpoint and two projects.
1. What Rust is, and why it exists
1.1 The problem being solved
For about forty years, systems software had two options:
- Manual memory management (C, C++). Fast and predictable, with control over layout and no runtime. Every pointer is a promise the programmer made, and the compiler believes it. The C guide's Part VIII shows how often those promises are broken.
- Garbage collection (Java, Go, C#). Memory safe, but it needs a runtime, adds pauses and memory overhead, and gives up some control over layout. Go §27 shows how much engineering Go puts into keeping that cost small.
The cost of the first option is measured. Microsoft (2019) and the Chromium project (2020) each reported that about 70% of their serious security bugs were memory-safety bugs: use-after-free, buffer overflow, double free, and uninitialized reads. Those are exactly the bug classes in the C guide's UB list.
Rust's claim is a third option: memory safety and data-race freedom, checked at compile time, with no garbage collector and no mandatory runtime. The compiler tracks who owns each value and who is borrowing it. It rejects programs where those rules could be broken.
1.2 The design goals, and how they show up in the syntax
| Goal | Where you see it |
|---|---|
| Memory safety without GC | ownership, borrowing, lifetimes (Ch 6–8, 19) |
| No data races | Send and Sync traits; Mutex<T> owns its data (Ch 28–29) |
| Zero-cost abstractions | generics are monomorphized; iterators compile to the same loop you would write by hand (Ch 16, 21) |
| Explicitness | no implicit numeric conversions, no null, no exceptions, mut is opt-in |
| Make illegal states unrepresentable | enums with data plus exhaustive match (Ch 9) |
| Escape hatch with a fence around it | unsafe blocks: you can still do anything C can, in a marked, auditable place (Ch 33) |
1.3 What Rust deliberately does not have
- No null. Use
Option<T>. The compiler forces you to handleNone. - No exceptions. Recoverable errors are values (
Result<T, E>). Panics exist for bugs, not for control flow. - No inheritance. Use traits and composition, as in Go.
- No garbage collector. Memory is freed at a point the compiler decides statically: when the owner goes out of scope.
- No implicit numeric conversion.
u8 + u32is a compile error. Writex as u32oru32::from(x). - No uninitialized variables in safe code. Reading a variable before it is assigned is a compile error, not C's UB.
1.4 War story
In 2019 a common class of bug in a large C++ media pipeline looked like this:
a decoder kept a pointer into a buffer and a background thread reallocated
the buffer. The pointer now pointed into freed memory. The crash appeared in
roughly one in a million frames, in production only, with a stack trace that
pointed at innocent code. In Rust, keeping a &[u8] into a Vec<u8> while
another part of the program calls push on it is a compile error
(E0502, Ch 7). Sharing the Vec with another thread at all needs an
Arc<Mutex<Vec<u8>>>, or the compiler refuses. The bug is impossible to
write in safe Rust, so nobody has to find it.
Lesson: Rust does not make you a better programmer at runtime. It refuses to compile a large class of programs that a careful reviewer would also reject, so the careful review happens on every build.
cargo new hello && cd hello && cargo run
# Compiling hello v0.1.0
# Finished `dev` profile [unoptimized + debuginfo] target(s)
# Running `target/debug/hello`
# Hello, world!