Performance & Memory
This course has repeatedly claimed "zero runtime cost" — the borrow checker (Course 1), lifetimes and monomorphized generics (Course 2). This chapter examines that claim directly, plus the practical tools for measuring performance in real Rust code.
What "Zero-Cost Abstraction" Actually Means
The precise definition, adopted as a Rust design principle: "what you don't use, you don't pay for; what you do use, you couldn't hand-code any better." This does not mean "free" in an absolute sense — an abstraction still costs whatever the equivalent hand-written code would cost. It just adds no extra overhead on top of that. Every concrete instance from this course fits this precisely:
- Ownership/borrowing (Course 1, Ch.3–4) — checked entirely at compile time, zero runtime check
- Generics (Course 2, Ch.3) — monomorphized, byte-for-byte identical to hand-written per-type code
- Iterators (Course 1, Ch.8) — compile down to a tight loop, no allocation overhead from the abstraction itself
Stack vs. Heap, Revisited
Course 1 Chapter 3 introduced this briefly; here's why it actually matters for performance. Stack allocation is just moving a pointer — extremely fast, no allocator involved at all. Heap allocation goes through the allocator — real bookkeeping, genuinely slower. A practical habit, not a hard rule: prefer stack-allocated data (fixed-size arrays, Copy types) where reasonable; reach for heap types (Box, Vec, String) when size is genuinely unknown at compile time or ownership needs to move around.
Profiling Tools
cargo build --release— always profile release builds; debug builds disable most optimizations and can be dramatically, misleadingly slowercriterioncrate — proper statistical benchmarking, not a one-off stopwatch timerperf(Linux) / native OS profilers — CPU profiling, flame graphsvalgrind --tool=massifor similar — heap profiling, memory usage over time
Contrast With Go's GC-Based Approach
This is the full circle back to Course 1 Chapter 1's founding question: how does Rust get memory safety without a GC? Go's garbage collector gives automatic memory management, at real cost: unpredictable pause-time risk (even with Go's modern low-latency concurrent GC, some structural risk remains), continuous background CPU for collection cycles, and typically higher baseline memory usage (a GC often keeps more memory reserved than strictly needed, to reduce collection frequency). Rust's compiler-verified model gives deterministic, GC-pause-free performance and typically lower memory overhead — at the cost of a real learning curve, and genuinely more upfront work at the type-design stage to get ownership right, rather than letting a collector handle it later.
| Go (GC) | Rust (Ownership) | |
|---|---|---|
| Memory management | Automatic, runtime GC | Compile-time verified, deterministic |
| Pause-time risk | Structurally present, even if minimized | None — no GC exists |
| Baseline memory | Typically higher (GC headroom) | Typically lower |
| Upfront cost | Lower — write code, let GC handle it | Higher — ownership design happens upfront |
Neither is simply better — two legitimate, different answers to the same underlying memory-management problem, each with real, honest trade-offs.
Zero-Cost Abstraction
No extra overhead beyond what hand-written equivalent code would already cost.
Stack vs. Heap
Stack: pointer bump, near-free. Heap: real allocator overhead — a genuine performance-relevant choice.
Release Builds + criterion
Never profile debug builds; use proper statistical benchmarking, not a stopwatch.
Go's GC Trade-offs
Automatic and simpler upfront, at the cost of pause-time risk and higher baseline memory.
--release.
Coding Challenges
Write a short explanation of why Vec
Given a choice between [i32; 100] (a fixed-size stack array) and Vec
Trace the "zero-cost abstraction" thread across this entire 3-course track: name one specific example each from Course 1, Course 2, and this chapter, and explain in one sentence what each one avoids paying for at runtime.
📄 View solutionChapter 5 Quick Reference
- Zero-cost abstraction: no overhead beyond what hand-written equivalent code would already cost — not "free"
- Stack allocation — a pointer bump, near-instant; heap allocation — real allocator overhead
- Always benchmark with cargo build --release — debug builds can be dramatically, misleadingly slower
- criterion for statistical benchmarking; perf/OS profilers for CPU; valgrind --tool=massif for heap profiling
- Go's GC: automatic, simpler upfront, real pause-time risk and higher baseline memory
- Rust's ownership: deterministic, zero GC runtime cost, more upfront ownership-design work
- Ownership/borrowing, lifetimes, monomorphized generics, and iterators all obey the same zero-cost principle — one coherent philosophy, not separate tricks
- Next chapter: Capstone — building a real CLI tool combining ownership, error handling, traits, and crates