Skip to content

Rust — Learning Library

A learning library for Rust, built the same way as its sibling star-voting-library ↗: one idea per page, and every claim backed by a program that actually runs.

No page here hand-types what a program prints. Each lesson links a real .rs file; a tool compiles it, runs it, checks the output against a recorded answer key, and pastes that verified output into the page. CI fails if any of the three drift apart. So when a page says "this prints Some(12)", that is not a promise — it is a test result.

📖 Read it as a site: https://masiarek.github.io/rust-learning-library/

Start here

New to Rust entirely? 00_Start_Here/ is the plan: which three free resources to use, in what order, and why each covers what the other two cannot.

Already writing Rust? Two lessons to begin with:

Lesson What it teaches
Running a scratch program How to run a .rs file at all — rustc alone, cargo new, and the edition flag Cargo would have passed for you
Option vs Result When absence is normal and when it is a failure — and the one question that decides which type you want

01_Foundations/ is the map of everything a first week of Rust needs, now split across seven sections by subject rather than kept in one flat run: first programs, control flow, structs, Option and Result, ownership, strings and numbers and bytes. Lessons about the toolchain rather than the language — cargo, build profiles, compile times — are in 05_Tooling/; the ones that assume the foundations — shared state across threads, unsafe, FFI — in 09_Advanced/.

25_Control_Flow/ is the if, match and three loops every program is made of. It sits right after the first programs because it needs exactly one idea from them — that a block has a value — and gives back the thing that makes Rust control flow read differently from its C-shaped equivalent: these are expressions, so let size = if n < 10 { "small" } else { "large" }; needs no ternary and no mut. Stubs today, outlines with their traps written down; blocks and scopes themselves are already taught, in a block is an expression.

13_Enums/ and 12_Traits/ sit directly after the foundations, because between them they are what the rest of the language is made of. Enums come first: anyone leaving the foundations has already used several of them — Option and Result among them — without being told that the feature has a name, or that a match which forgets a variant is a build error. Traits are the other half, and the same argument applies twice over: Copy, Display, Iterator and From all turn up in the earlier pages long before anything explains what they have in common.

30_Pattern_Matching/ is one feature taught once instead of six times. match arms, if let, while let, let else, every let, and every function parameter are all the same thing — a shape matched against a value, binding the pieces by name — and what differs between them is only what happens when the shape does not fit. Stubs today. Three pieces of the topic stay where they already are, on the pages a reader meets them from: if_let, while_let, and the lowercase arm name that is not a comparison but a binding that matches everything.

22_Generics/ follows both, because it needs both: <T> is how one definition serves every type, and a trait is what tells the compiler which types are allowed. It is also the section that explains the brackets in Option<T>, Vec<T> and Result<T, E> — read a hundred times by then, and nowhere defined.

23_Closures/ and 24_Iterators/ are the pair of features every section above has already been spending. unwrap_or_else(|| 0) and for row in &rows are both on page one of this library and neither is explained there. Closures cover which of the three Fn traits a closure gets and why the compiler cares, what move moves, and why a closure that captured nothing is smaller than a function pointer; iterators cover why a chain of adapters allocates nothing, which of three doors a for loop picked for you, and what happens when you write next yourself. They are two sections rather than one because the sidebar is alphabetical, and a reader looking for iterators can name iterators.

One section is not about Rust at all. 11_Unix/ is the shell you run the compiler from — fzf for picking a file instead of typing its path, fd and rg for finding one — and it earns its place because two of those three are themselves Rust programs, measured here against the Unix tools they stand in for.

20_Compilers/ is the layer under all of it. Most of what turns a .rs file into a binary was not written for Rust — rustc parses and borrow-checks, and then LLVM optimizes, LLVM generates machine code, and a linker nobody on the Rust team maintains finishes the job. The section covers the compile-time/run-time line, what an optimizer is allowed to do to your loop, what LLVM actually is, what the linker does with the blanks rustc leaves, and the same machinery aimed backwards as obfuscation.

Five sections in between follow one long arc: turning a snippet into a program somebody else runs. 02_Errors/ is what a failure does on its way out; 03_Command_Line/ is what the program was handed on the way in; and 04_Files/, 06_Data/ and 07_Clients/ are the three things outside it that can say no. Most of those pages are stubs today — one outline and a set of questions per page, with no runnable example behind them yet, each marked as such at the top. They exist so the arc has a shape, and a permanent URL, before the prose does; a page graduates by acquiring an example and losing its notice.

21_Observability/ picks up where that arc ends: the program is now the service somebody else calls, and the only account of what happened to one request is whatever it chose to write down — a span, a structured line, a counter, and a header carrying the trace to the next process. Stubs as well, and the hardest ones here to finish, because every checked example in this library compiles with rustc alone and observability in Rust is a crate story. The section README says how it intends to close that gap rather than skip it.

RUST_BY_EXAMPLE.md is a map of a different kind: Rust by Example ↗ chapter by chapter, with the lesson here that covers each one, and an honest list of what it does not.

Five topics are big enough to have a map of their own rather than a single page: OPTION.md, SHADOWING.md, STRUCTS.md, STRINGS.md and TOOLCHAIN.md each collect every lesson on their subject in reading order, and say what the idea is before the syllabus starts. The first four cross section boundaries because the lessons do; the fifth sorts one section by the problem you actually have.

ERRORS.md is a map you arrive at from the opposite direction: not "what shall I learn next" but "rustc printed E0502 and I am stuck". It lists the 58 error codes this library teaches with the lesson that explains each one, and it is honest about the two that have no single home — E0308 most of all, which is what rustc says when almost anything is wrong.

A lesson can end with a ## Po polsku section — the same idea framed the way Polish-language material frames it, with the Polish terminology and the exact phrases to search for. It is written in Polish rather than translated into it, and it keeps the English term in sight, because rustc and every crate's documentation speak English. POLSKI.md holds the terminology table and says which terms are anchored in Tour of Rust's Polish chapters ↗ and which this library had to choose for itself. Ownership has the sections on every page; the rest of the library does not have them yet.

Every lesson explains; some of them also ask you to type. Those exercises are collected in KATAS.md, which is the only place they are ordered — each kata itself sits on the page for the topic it teaches, with a solution CI compiles and runs.

There is also a slow, optional thread running through them: the long way round to a column summary, which sequences a handful of lessons so that each one is the next thing Rust wants to teach, and the running example happens to be a small file-summarising tool. Every rung stands alone; the tool is the excuse.

What is not written yet has its own list: TODO.md holds the strings vocabulary — 198 terms a reader meets in real Rust string code — with a box per term, ticked when the term is answerable from a lesson or a glossary entry rather than when it has a folder of its own.

The course, in order

The sidebar is sorted alphabetically, because that is how you find a section you can already name. This is the other question — what should I read next? — and it is the order these were written to be read in:

# section what it is for
1 Start here The three outside courses this library is a companion to
2 Foundations The map of a first week, pointing into the seven sections below
3 First programs Running a .rs file at all, and what the punctuation in it means
4 Control flow if, match and the three loops — the ones that have values
5 Structs A compound type of your own, before the two the library leans on
6 Option and Result The two enums everything returns, and the dozen ways to open them
7 Ownership Who owns the value — moves, borrows, and what a shadow does
8 Strings Text: the owner and the view, and the bytes underneath
9 Numbers and bytes The unit all of that is counted in, down to the float that cannot hold your value
10 Collections The six types a program is made of — and why a function should take &[T], not &Vec<T>
11 Pattern matching A pattern is a shape, not a comparison — one feature wearing six syntaxes
12 Enums The feature Option and Result were made of all along
13 Traits The other half the language is built from — and how a call reaches one
14 Generics <T>: one definition per idea, instead of one per type it is used with
15 Closures A function that carries values with it, and the three traits that say how often you may call it
16 Iterators The sequence that computes nothing until somebody asks, and the three doors onto a collection
17 Errors What a failure does on its way out of a program
18 Command line What the program was handed on the way in
19 Files The filesystem — the first thing outside the program that can say no
20 Tooling cargo and the rest of the toolchain, rather than the language
21 Data Serialization, and the round trip through JSON
22 Clients The network — the last thing outside the program that can say no
23 Observability What the service says about itself, once somebody else depends on it
24 Interfaces Putting a face on it
25 Advanced What needs the foundations: threads, unsafe, FFI
26 Compilers The layer under all of it, three quarters of which is not Rust
27 Resources Books, essays and exercises outside this library
28 Unix The shell you run the compiler from — two of its three tools are Rust
29 C and C++ Nine bugs the compiler refuses to build, and C++'s own two replies — read it any time after Ownership
30 Debugging The map to what is left over once the compiler has caught the rest — read it the first time something surprises you
31 Time and benchmarking Two clocks, a duration that cannot go negative, and the hint that keeps a benchmark honest — the Rust twin of the C++ library's first chapter; read it any time after Compilers

Nothing enforces this order and no page depends on it; skipping around is fine. It is here because a sidebar can be sorted one way only, and A–Z answers the more common question.

Skipping around is fine; moving a row is not always. Nothing enforces the order mechanically, but parts of it are load-bearing prose: row 9 opens "The unit all of that is counted in", a back-reference to the bytes in row 8, and row 8 follows Ownership because the strings arc opens on "the pair of types the ownership pages were secretly about all along". A section's own README is the other constraint — what it links to as where it goes next has to come after it. Check those before inserting or reordering, because none of them is a link and mkdocs --strict cannot see any of it.

How the library works

17_Option_and_Result/
  option_vs_result/
    README.md                       the lesson  (prose + code + a generated output block)
    examples/
      option_vs_result.rs           the program the lesson is about
      option_vs_result.out          its recorded output — the answer key
tools/run_examples.py               compiles, runs, compares, and refills the pages

A lesson marks the spot where output belongs and lets the tool fill it:

<!-- output:option_vs_result -->
<!-- /output -->

Inside the markers is generated; outside is yours. Run the tool after any change:

python3 tools/run_examples.py

Running things

Task Command
Verify every example and refresh the pages python3 tools/run_examples.py
Accept new output as the answer key python3 tools/run_examples.py --update
Check without writing (what CI runs) python3 tools/run_examples.py --check
Preview the site locally uv run --group docs mkdocs serve
Run one example by hand rustc --edition 2024 path/to/example.rs -o /tmp/x && /tmp/x
Give an IDE a Cargo.toml to read python3 tools/write_cargo_toml.py

Only rustc and Python 3.11+ are needed for the examples; uv is needed only to preview the site.

That last row is for reading the library rather than running it. Every example is compiled on its own by bare rustc, so the repo carries no Cargo.toml — and without one RustRover opens every .rs file under "Project not associated with a Cargo.toml file": no inferred types, no go-to-definition, no Run button. tools/write_cargo_toml.py writes a gitignored manifest for your machine listing every .rs file in the checkout, scratch files included, as a binary; attach it once in RustRover, or point rust-analyzer at it from VS Code or Zed.

Adding a lesson

See CONTRIBUTING.md — it is short, and it is mostly about the one rule above.