12 - Reading Other People's Code¶
What this session is¶
About 45 minutes. You'll learn the strategy for reading code you didn't write. Same shape as the equivalent page in Go/Java/Python from scratch - adapted for Rust idioms.
The trick¶
When you open a new codebase, don't try to read it linearly. Pick a small thread; follow only it; let the rest stay fuzzy. A first read of a real Rust project takes 5 minutes and produces a one-paragraph mental map; not three hours and confusion.
Five-minute orientation¶
For any Rust project:
-
Read the README. What does this do? If unclear, the project is too unfinished.
-
List the top-level files/directories:
Cargo.toml- manifest.Cargo.lock- pinned versions (for binaries).src/- source.src/lib.rs- library entry point.src/main.rs- binary entry point.src/bin/- multiple binary entries.tests/- integration tests.examples/- runnable examples.benches/- benchmarks (criterion).docs/- long-form docs (some projects).-
.github/workflows/- CI. -
Open
Cargo.toml. Name? Version? Dependencies? Tells you the ecosystem. -
Find the entry point. For a library:
src/lib.rs. Read it - often a list ofpub mod foo;declarations and re-exports. That tells you the public API. -
Read one test or example. Tests show you what the code is supposed to do, with concrete code.
After this: write a one-paragraph summary. If you can't, repeat.
Tools for reading Rust¶
rust-analyzerin your editor - go-to-definition, find-references, inline type display. Use these constantly.cargo doc --open- generate and open the project's documentation in your browser. Sorted, navigable, includes all public APIs.- docs.rs - the public hosted documentation for every published crate. Search "
docs.rs". cargo test --no-run- compiles tests without running them. Useful for "does this build" before diving in.grep -r 'pattern' src/- old-school but works.cargo expand(cargo install cargo-expand) - shows what macros expand to. Useful when you encounter a#[derive(...)]or other macro you don't understand.
A worked example: reading serde_json::Value¶
serde_json::Value is the dynamic JSON value type. Let's say we just encountered it.
cargo doc --open(in a project using serde_json) - search forValue.- Documentation says: "Represents any valid JSON value." Variants:
Null,Bool(bool),Number(Number),String(String),Array(Vec<Value>),Object(Map<String, Value>). - So
Valueis an enum (page 05 of this path!). Read a few of its methods:is_string(),as_str(), etc. - Look at examples in the docs. Most public docs include them.
Five minutes, mental model.
Things you'll see that look scary in Rust¶
- Lifetime annotations (
<'a>,&'a str) - page 06. Most of the time the compiler infers; you see them in library code. For reading: "this reference must live as long as that." - Macros (
println!,vec!,derive!) - the!marks them. Macros expand to code at compile time. For reading: treat as "a function with magic." Box<dyn Trait>- page 09. Heap-allocated trait object.Pin<Box<...>>- used in async. Means "this can't be moved in memory after creation." Don't worry about it unless you're writing async runtimes.asyncandawait- async/await syntax. Used heavily in web/networking. Functions return a "future" that runs when polled by a runtime (Tokio, async-std).unsafe { ... }- escape hatch. The author takes responsibility for memory safety inside. Recognize; skim.impl Traitin return position -fn foo() -> impl Iterator<Item = i32>. Means "returns some type that implements Iterator." The actual type is hidden from callers.Rc<RefCell<T>>/Arc<Mutex<T>>- shared ownership with interior mutability. Used when you need shared mutable state across functions or threads.- Generic chaos:
<T: Iterator<Item = U>, U: Clone>- complex generic bounds. Skim; understand the gist. - Procedural macros (
#[proc_macro_attribute], etc.) - code that generates code. Used by serde, tokio, actix. Recognize; treat as "magic."
You will hit things you don't recognize. Knowing when to skim past vs dig in is the skill.
Reading vs understanding¶
You can read code without deeply understanding why it's shaped that way. A first PR to a project often involves reading 1000 lines, understanding 100, modifying 5. That ratio is normal.
Exercise¶
No coding this time. Reading.
Pick a small Rust project on GitHub:
fdehau/tui-rsor its successorratatui-org/ratatui- terminal UI library. Reasonable size.BurntSushi/ripgrep- fast grep. Bigger but very well-organized.clap-rs/clap- CLI parser. Substantial but excellently documented.- For something tiny:
seanmonstar/num_cpus- one purpose, one function (num_cpus::get()).
Apply orientation:
1. README - what does it do?
2. Layout - what files exist?
3. Cargo.toml - dependencies?
4. Find the entry point. Trace the most-public function for 5 minutes.
5. Read one test or example.
Write a paragraph: what does this do? How is it organized? What surprised you?
What you might wonder¶
"What if I don't understand?" Note, skip, keep going. Often what confused you on day 1 makes sense after a week.
"What about huge projects like rust-analyzer itself?"
Same technique, just more applied. Read the top-level architecture docs first; then pick one component and orient on just that.
Done¶
- Apply five-minute orientation to any Rust project.
- Use rust-analyzer,
cargo doc, docs.rs. - Distinguish reading from understanding.
- Recognize common "looks scary, isn't" patterns.