Reading Rust

Part 5 · Unsafe & real code

Reading a real crate

A repeatable procedure for understanding code you have never seen, built on the one model of program comprehension that actually tells you what to look at.

60 minBlock Modeltop-down comprehensionauthentic task
What this lesson is trying to break (2)
  • You understand a codebase by reading it from the top of each file.
  • Understanding means understanding every line.

This is the course’s stated goal, made concrete: you are handed a crate you have never seen and asked what it does and whether to depend on it.

The model

Program comprehension has been studied for forty years, and the most useful result for our purposes is Carsten Schulte’s Block Model. It says understanding a program is not one activity but a grid, and that people get stuck because they read in one cell and never move.

Varies: the two independent axes — granularity down, perspective across

GranularityText surface
what it says
Program execution
what it does
Function / purpose
what it is for
Atoms
keywords, expressions
&mut self, ?, implthis borrows exclusively; this returns earlyrarely meaningful alone
Blocks
functions, impls
a function signaturewhat one call doesthe job this function has
Relations
how blocks connect
the trait a type implementsthe call graph, who owns whatthe design — why these pieces
Macro structure
the whole crate
the module treethe lifecycle of a requestthe problem it solves

Beginners live in the top-left cell and read downward. Experts start bottom-right and move down-left only when they need to. That is the difference this lesson is trying to install.

The practical instruction: start at the bottom-right and work up-left. Understand the purpose before the structure, the structure before the functions, the functions before the syntax. Reading a file from line 1 gives you atoms with nothing to attach them to, which is why it feels like effort and produces nothing.

The procedure

1. Purpose (macro / function). Read the README and the docs.rs front page. Answer in one sentence: what problem does this solve, and for whom? If you cannot, stop and find out — everything else is wasted until you can.

2. Shape (macro / text). Open src/lib.rs. Do not read the code; read the mod and pub use lines. That is the author’s own map of the crate. The re-exports tell you which types matter — anything re-exported at the root is part of the intended public surface.

3. Nouns (blocks / purpose). On docs.rs, look at Structs and Enums. What things exist? Usually two or three matter and the rest are supporting. For serde: Serializer, Deserializer. For tokio: Runtime, JoinHandle.

4. Verbs (relations / purpose). Look at Traits. In Rust, traits are the design — they say what the crate expects you to plug in and what it promises. A crate’s central trait is usually its whole idea in one page.

5. One path (relations / execution). Pick the single most typical use — the first example in the README — and follow it end to end. Use docs.rs’s source links to jump into implementations. Follow exactly one path and resist every branch.

6. Details (atoms). Only now, and only where you need them.

Tools

Do not read Rust in a plain text view. The tooling is unusually good and it does most of the work.

  • docs.rs — every published crate, every version, with a source link on every item. Reading the source in context of its documentation is far better than reading the repo.
  • cargo doc --open — the same for your project including your private items and your exact feature flags.
  • cargo tree — why is this dependency here? Also cargo tree -i <crate> to invert and find who pulled it in.
  • cargo expand — see through macros. Essential for anything using derives or attribute macros heavily.
  • rust-analyzer — go-to-definition, and the inlay type hints. Turn hints on. When reading unfamiliar generic code, having the inferred concrete types shown inline is the difference between decoding and reading.
Beaconsee this → think this, without decoding it

src/lib.rs with only `mod` and `pub use` lines

A facade. The author has separated the public API from the implementation. The re-export list is a curated statement of what you are meant to use, and anything not re-exported is internal even if it is technically pub.

Read the re-exports as the crate’s table of contents. It is the highest information-per-line file in most crates.

Worked example — a procedure run

Take once_cell, which is small enough to hold in your head.

Step 1 — purpose

A cell that can be written exactly once, for lazily-initialised globals.

The problem it solves: Rust has no way to run code before main, and static items must be const-initialised. So a global that needs runtime setup — a compiled regex, a config, a connection pool — has nowhere to live.

Note that std has since absorbed the core of this as OnceLock and LazyLock. Knowing that a popular crate has been partly upstreamed is exactly the kind of ecosystem awareness that is useful in a dependency review.

Step 2 — shape

Two top-level modules: sync and unsync.

That single fact tells you most of the design, if Part 4 landed. unsync is the single-threaded version with no atomics; sync is the thread-safe one and costs more. It is the same Rc/Arc and RefCell/Mutex split from 4.1, applied again.

You have learned something real about the crate without reading a line of code.

Steps 3 and 4 — nouns and verbs

Two types per module: OnceCell<T> and Lazy<T>.

  • OnceCell<T> — empty or full; get_or_init(f) fills it if needed.
  • Lazy<T> — an OnceCell plus the initialiser, so it derefs to T and initialises on first access.

No central trait; this is a data-structure crate rather than an abstraction crate. That absence is itself informative — compare with serde, where the traits are the crate.

Step 5 — one path, and what to look for

Follow get_or_init. The interesting question, given Part 2, is: how does a method taking &self write to the cell?

The answer is interior mutability (2.6) — UnsafeCell inside, with a synchronisation mechanism ensuring exactly one initialisation even under concurrent access, and a // SAFETY: argument for why handing out &T afterwards is sound.

You now know what the crate does, why it has the shape it has, where the unsafe is, and what invariant that unsafe depends on. That is a complete-enough understanding to depend on it, review a PR touching it, or discuss it — and it took reading a few dozen lines.

Your exercise

Three crates, increasing difficulty. Ninety minutes each, procedure in writing.

  1. once_cell — follow the walkthrough above, then check yourself against the source.
  2. bytes — a byte buffer with cheap cloning. The question to answer: how is Bytes::clone cheap, and what does that imply about ownership?
  3. ripgrep — a large real application. Do not attempt to understand it all. Answer: what are the top-level crates in the workspace, what does each do, and where does a single search actually execute?

For each, produce four sentences: what it does, its central abstraction, one design decision the author made and why, and one thing you would ask them.

That four-sentence summary is the actual deliverable of this course. It is what “reading Rust with deep understanding” means in practice, and it is what lets you walk into a design review having formed a view.

Explain it to yourselfwrite it — thinking it does not count

Do exercise 1 now, before moving to Part 6. Write the four sentences here.

If you skip this, the rest of the course is theory. This is the transfer task — the point where the model either works on unfamiliar code or does not.

What you should now be able to say

  • Comprehension is a grid, not a line. Start at purpose, end at syntax.
  • The reading order: purpose → module tree → types → traits → one path → details.
  • lib.rs re-exports are the crate’s table of contents.
  • Tools do most of the work: docs.rs source links, cargo tree, cargo expand, rust-analyzer inlay hints.
  • The deliverable is a four-sentence summary, not total understanding.

Next: 6.1 — The lexicon and the discourse.

In the wildgo read the real thing now, while it is fresh
  • once_cell — small, real, excellentStart here. Tiny public API, genuinely useful, and the internals combine atomics, UnsafeCell and a safe wrapper — everything from Parts 2 and 5 in one small place.
  • ripgrepThe final exercise. A real, large, widely admired application. Do not try to understand all of it — apply the procedure, spend ninety minutes, and write the four-sentence summary. That is the skill.
Scheduled for recall3 items added to your review queue

These are not shown to you now on purpose. They will surface in Review tomorrow, mixed in among items from other parts of the course — not grouped by topic. Sorting out which idea applies is the part that transfers.