Learning Rust by reading real Rust
The fastest way to learn Rust from open source is to read projects in increasing order of how much they fight the borrow checker: start with a CLI tool that mostly owns its data, move to a library with a public API and lifetimes in its signatures, then read an async network service, and finally something with real unsafe blocks. Ownership is the thing to watch throughout — in production Rust the interesting decisions are almost always about who owns what and for how long.
Why reading this kind of code is worth the time
- Rust's difficulty is concentrated in a small number of decisions — ownership, lifetimes, and when to reach for Arc or a channel — and those decisions are invisible in tutorials, which use examples small enough that any answer works.
- Error handling in real Rust looks nothing like `unwrap()`. Reading how a mature crate defines its error type, and how that type changes as it crosses module boundaries, teaches more than any chapter on the subject.
- The `unsafe` blocks in a well-regarded crate are the best available writing on when unsafe is justified, because each one comes with a comment arguing for itself.
A reading sequence
Ordered by difficulty, not importance. Each stage assumes the one before it.
Start
A command-line tool of a few thousand lines — a formatter, a file utility, a small parser.
Look for: How arguments become a config struct, and how that struct is passed down. Most of it will be owned values, which is why it reads easily.
Next
A library with a published API and a real error type.
Look for: The signatures on the public functions. Every `&` and `'a` in them is a decision someone made about who keeps the data alive, and the docs usually explain why.
Then
An async service — an HTTP server, a job runner, a client for some protocol.
Look for: What is held across an `.await`. That is where `Send` bounds, `Arc<Mutex<…>>` and the occasional deadlock all come from.
Deeper
A data structure or runtime crate that uses `unsafe`.
Look for: The safety comment above each unsafe block. Read the argument before the code; then check whether the code actually upholds it.
Concepts you will keep meeting
- Ownership and borrowing
- Lifetimes in public APIs
- Error types and the `?` operator
- Traits and generic bounds
- Interior mutability
- Async and `Send`/`Sync`
- When `unsafe` is justified
Questions to ask of any codebase in this area
Reading with a question in mind is the difference between studying code and scrolling through it.
- 1.Where does this crate define its error type, and what does it convert from?
- 2.Which types are `Clone`, and does cloning them cost anything?
- 3.What is the largest struct, and who owns it at runtime?
- 4.Is there an `Arc` here that could have been a borrow, and why was it not?
Common questions
Get a matching repository every day, with a reading plan
Repo Dive does what this guide describes, daily and automatically: it finds a repository matching what you want to learn, explains why it chose that one, names the files to read first and asks three questions about it. Free, and it tracks what you have already studied so the picks keep moving forward.
Start with GitHubRelated guides
- Learning backend architecture from production code
How to read backend codebases for their architecture: request flow, boundaries, caching, background work, and the questions that reveal how a service is really put together.