We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
Rust roadmap
Ordered runs of problems, and nothing else: no reading, no progress bar, just the sequence. For a syllabus that interleaves written explainers and can be worked through with friends, see tracks.
Rust: zero to hero
Every rust track in course order, grouped into the syllabus's ten build phases. Ownership is split across three separate tracks on purpose, threads come before async because you build the executor, and unsafe comes last.
342 problems
1
One file, one function, one gate
Easy
2
How to read a rustc error message
Easy
3
Naming conventions and the style gate
Easy
4
let, immutability by default, and mut
Easy
5
Shadowing: rebinding is not mutation
Easy
6
Integer types: widths, signedness and usize
Easy
7
Numeric literals: suffixes, underscores, other bases
Easy
8
Integer overflow: debug panics, release wraps
Medium
9
checked, wrapping, saturating, overflowing
Medium
10
Integer division and remainder: the signs will surprise you
Medium
11
Floating point: f32, f64, NaN, and why == is a trap
Medium
12
bool and char
Easy
13
Casting with as: truncation, sign reinterpretation, saturation
Medium
14
From, Into, TryFrom: conversions that cannot lie
Medium
15
Statements vs expressions, and the semicolon that changes the type
Easy
16
Functions: parameters, return types, early return
Easy
17
if is an expression
Easy
18
loop, while, for, and break with a value
Easy
19
Loop labels and labeled blocks
Medium
20
Ranges: .., ..=, and range patterns
Easy
21
Tuples and destructuring
Easy
22
Arrays: the length is part of the type
Easy
23
Vec<T>: the growable one
Easy
24
Slices &[T]: the shape that unifies arrays and Vec
Medium
25
String vs &str, part 2: functions that take and return text
Medium
26
Strings are UTF-8: bytes, chars, and the indexing panic
Hard
27
The three rules of ownership
Easy
28
Stack, heap, and what a variable actually holds
Easy
29
Move semantics: the relay
Easy
30
Reading E0382 line by line
Easy
31
Copy: the types that don't move
Easy
32
Clone: explicit, deep, and never free
Easy
33
Ownership through function calls
Easy
34
Giving ownership back
Medium
35
Partial moves: taking one field and leaving the rest
Medium
36
E0509: a Drop type is an atom
Hard
37
drop(x) is just a function
Easy
38
Drop order is specified
Medium
39
Moving out of &mut: take, replace, swap
Medium
40
Conditional moves and drop flags
Hard
41
Ownership inside loops
Medium
42
Shadowing is not moving
Easy
43
Method receivers and the consuming builder
Medium
44
Leaking is safe
Hard
45
Move or borrow: the two ways to pass a value
Easy
46
&mut: exclusive access, and why `mut` appears twice
Easy
47
How to read a borrow-check diagnostic
Easy
48
Non-lexical lifetimes: a borrow ends at its last use
Easy
49
E0502: cannot borrow as mutable because also borrowed as immutable
Easy
50
E0499: cannot borrow as mutable more than once
Medium
51
The owner is frozen too: E0503, E0505, E0506
Medium
52
Slices are borrows
Easy
53
&str not &String, &[T] not &Vec<T> — deref coercion and ptr_arg
Easy
54
ref, ref mut, and default binding modes in edition 2024
Medium
55
Iterator invalidation, and the bug class that stopped existing
Medium
56
Indexing in a loop is usually a borrow-checker workaround — and clippy knows
Medium
57
iter_mut(): mutating every element without ever holding two &mut
Medium
58
Split borrows: the checker reasons about places, not variables
Medium
59
split_at_mut and the disjointness-proof APIs
Medium
60
Reborrowing: why &mut sometimes moves and sometimes doesn't
Hard
61
Two-phase borrows: why v.push(v.len()) compiles
Hard
62
What a &mut parameter actually promises
Medium
63
NLL Problem Case #3, the entry API, and the limits of today's checker
Hard
64
Closures hold their borrows
Medium
65
Interior mutability: moving the check from compile time to run time
Medium
66
Structs: naming your data
Easy
67
Field init shorthand and struct update syntax
Easy
68
Tuple structs and unit structs: the other two shapes
Easy
69
impl blocks: associated functions, methods, Self and associated constants
Easy
70
Deriving traits, and when derive gets the bounds wrong
Easy
71
Enums are real sum types
Easy
72
match and exhaustiveness: the compiler as a checklist
Easy
73
The wildcard trap: `_` arms freeze your enum against the future
Medium
74
Literal, range and or-patterns
Easy
75
Binding with @, and what or-patterns demand of bindings
Medium
76
Destructuring structs and nested patterns
Medium
77
Slice and array patterns, and rest @ ..
Medium
78
Match guards, and why they don't count toward exhaustiveness
Medium
79
Refutability, and every place a pattern can appear
Medium
80
if let, while let, and let ... else
Easy
81
Let-chains: the edition-2024 feature that deletes your nesting
Medium
82
matches! and the lints that push you toward it
Easy
83
#[derive(Debug)] and the three shapes of debug output
Easy
84
PartialEq and Eq: why floats can be compared but not Eq
Medium
85
PartialOrd and Ord: derived ordering follows declaration order
Medium
86
Hash and the Hash/Eq contract
Medium
87
Default, #[default] on an enum variant, and ..Default::default()
Easy
88
Discriminants, #[repr(u8)], as casts and mem::discriminant
Medium
89
pub on structs and enums behaves differently
Easy
90
Vec surgery: retain, dedup and extract_if
Medium
91
String building: push_str, write!, and the 590x cliff
Easy
92
Joining owned strings
Easy
93
Case-insensitive search without allocating
Easy
94
Zero-copy parsing: borrow, don't allocate
Medium
95
HashMap fundamentals: get, get_mut and the Hash/Eq contract
Easy
96
The Entry API: one lookup, not two
Medium
97
HashMap iteration order is random
Medium
98
BTreeMap range queries
Medium
99
Set algebra with BTreeSet
Easy
100
VecDeque and breadth-first search
Easy
101
BinaryHeap is a max-heap, and Reverse is the fix
Medium
102
Grouping without itertools: anagram buckets
Medium
103
windows and chunks are slice methods, not iterator adapters
Medium
104
Cow: borrow until you have to own
Medium
105
Cow in API design: impl Into<Cow<str>>
Hard
106
In-place mutation vs rebuild, measured
Easy
107
Reusing buffers instead of allocating per iteration
Medium
108
Everything you can collect into
Easy
109
Matching on Option
Easy
110
The Option combinator vocabulary
Easy
111
Eager vs lazy defaults
Easy
112
Borrowing through Option
Medium
113
Mutating an Option in place
Medium
114
Result, and why it is #[must_use]
Easy
115
Result combinators and map_err
Easy
116
Converting between Option and Result
Medium
117
Propagating with `?`
Easy
118
`?` on Option, and bridging to Result
Medium
119
Implementing std::error::Error
Medium
120
From impls: the machinery behind `?`
Medium
121
Error chains and source()
Medium
122
Boxed errors and downcasting
Medium
123
Adding context without anyhow
Hard
124
#[must_use] in depth
Medium
125
Collecting an iterator of Results
Medium
126
unwrap and expect discipline
Medium
127
Documenting failure: # Errors and # Panics
Medium
128
Traits as shared behaviour
Easy
129
Generic functions and monomorphisation
Easy
130
Trait bounds and why f64 is not Ord
Easy
131
where clauses and the bound you cannot write inline
Easy
132
A generic Stack, and conditional impl blocks
Medium
133
Display, Debug, and the ToString blanket impl
Easy
134
Supertraits and default methods
Medium
135
From, Into, and your first blanket impl with teeth
Medium
136
TryFrom, TryInto, and the type that cannot exist
Medium
137
AsRef, Borrow, and signatures your callers can actually use
Medium
138
Deref and the coercion you have been using all along
Medium
139
Deref polymorphism, and the day your method disappears
Medium
140
Operator overloading: + is just a trait method
Medium
141
Associated type or generic parameter?
Hard
142
impl Trait in argument position is not a generic
Medium
143
Returning impl Trait, and what edition 2024 changed
Hard
144
Blanket impls, and why you can never carve out an exception
Hard
145
The orphan rule and the newtype escape
Hard
146
Extension traits: adding methods to types you do not own
Medium
147
Sealed traits: public to use, closed to implement
Medium
148
Borrow it or own it: your first lifetime decision
Easy
149
Let elision do it
Easy
150
`longest`: writing your first `'a`
Medium
151
Two inputs, one output lifetime
Medium
152
Zero-copy tokenizer: `Parser<'a>`
Hard
153
Make the lint happy: `'_` in return paths
Easy
154
`T: 'static` in practice — a type-erased handler registry
Hard
155
Generics, traits and lifetimes in one signature
Medium
156
Closures 101: build a pipeline of boxed closures
Easy
157
Fn, FnMut, FnOnce: the closure hierarchy
Medium
158
move: when the closure must own its captures
Medium
159
iter, iter_mut, into_iter: all three, one Vec
Easy
160
IntoIterator: one bound that accepts five containers
Medium
161
The double-reference wall: &&x, copied and cloned
Medium
162
Laziness: prove the pipeline interleaves
Easy
163
map, filter, filter_map: parse host:port lines
Easy
164
fold and try_fold: a total that refuses to overflow
Medium
165
take_while and skip_while: split a message at the blank line
Easy
166
zip and enumerate: pair two lists and report the leftovers
Easy
167
peekable and next_if: write a tokeniser
Hard
168
flat_map: expand a range spec like "1-3,7,10-12"
Medium
169
scan: a running balance that stops at the floor
Medium
170
Implement Iterator by hand: the Collatz sequence
Medium
171
from_fn and successors: iterators without writing a struct
Easy
172
any, all, find, position: audit a list of records
Easy
173
min and max: the tie-breaking rule nobody reads
Medium
174
partition and unzip: two collections from one pass
Medium
175
collect and the turbofish: three targets, one source
Medium
176
size_hint and DoubleEndedIterator: an iterator with two ends
Hard
177
Write your own adapters, with an extension trait
Hard
178
Make your own collection iterable, all three ways
Hard
179
Chains versus loops: top-k, and a function that must not allocate
Medium
180
The FnMut borrow gauntlet: a log you can read afterwards
Medium
181
Modules are a tree you declare, not files you discover
Easy
182
Paths: absolute, relative, crate::, self::, super::
Easy
183
Everything is private by default: the two privacy rules
Easy
184
Restricted visibility: pub(crate), pub(super), pub(in path)
Medium
185
use declarations: nested groups, as, self, and _
Medium
186
Glob imports, shadowing, and ambiguity
Medium
187
Three namespaces: types, values, and macros
Medium
188
pub use re-exports and the facade pattern
Hard
189
The preludes — all five of them
Medium
190
macro_rules! scoping is not item scoping
Hard
191
Module file layout: foo.rs, foo/mod.rs, and #[path]
Medium
192
#[cfg], cfg!, cfg_attr and the new cfg_select!
Medium
193
Version requirements: caret, tilde, wildcard, pre-releases
Medium
194
Cargo features and the additivity rule
Hard
195
SemVer for Rust APIs: what actually breaks
Hard
196
Build your own #[test]: catching panics deterministically
Hard
197
Test selection: filters, #[ignore], and the libtest CLI
Medium
198
The stack, the heap, and `Box<T>`
Easy
199
Recursive types need indirection: E0072
Easy
200
`large_enum_variant`: boxing the fat variant
Medium
201
`Rc<T>`: shared ownership by reference counting
Medium
202
Inside `Rc`: `strong_count`, `ptr_eq`, `get_mut`, `try_unwrap`
Medium
203
`Rc::make_mut`: copy-on-write shared data
Hard
204
`Rc<str>`: interning without double indirection
Medium
205
`Cell<T>`: mutation by moving values in and out
Medium
206
`RefCell<T>`: borrowing checked at runtime
Medium
207
When `RefCell` panics: re-entrancy and borrows held too long
Hard
208
`let _ =` versus `let _x =`: the guard that dies instantly
Medium
209
RAII: build your own guard
Medium
210
`Rc<RefCell<T>>`: the workhorse, and when it is a smell
Hard
211
Reference cycles leak memory
Hard
212
`Weak<T>`: non-owning references that break cycles
Hard
213
`Rc::new_cyclic`: self-reference in one step
Hard
214
`OnceCell`: write-once interior mutability
Medium
215
`static mut` is dead: what edition 2024 changed
Medium
216
Indices as handles: the generational arena
Hard
217
Bake-off: the same tree, an arena and an `Rc` graph
Hard
218
Drop order in composite structures
Medium
219
Build `Cell<T>` and `RefCell<T>` from scratch
Hard
220
Box<dyn Trait>: trait objects, DSTs and dynamic dispatch
Medium
221
Dyn compatibility: the actual rules
Hard
222
Static vs dynamic dispatch, measured
Hard
223
Enum dispatch: the third option
Medium
224
Dispatch through Rc: Rc<dyn Trait> and shared behaviour
Medium
225
Trait objects and lifetimes: Box<dyn Trait + 'a>
Hard
226
Designing signatures: T, &T, &mut T, impl Into<T>
Medium
227
Where the allocations are: the counting allocator
Easy
228
Type sizes, enum layout and niche optimisation
Medium
229
repr(Rust) reorders your fields — and that's the point
Medium
230
repr(C): computing struct layout by hand
Medium
231
The other reprs: transparent, packed, align, integer enums
Hard
232
Memory layout and cache behaviour: flat arrays, AoS vs SoA
Medium
233
Capacity and pre-sizing, graded
Easy
234
Needless clone: the most common review comment in Rust
Medium
235
Sorting: sort vs sort_unstable vs sort_by_cached_key
Medium
236
Bounds checks: when they cost, and five safe ways to remove them
Hard
237
Auto-vectorisation and why floating-point addition blocks it
Hard
238
#[inline], #[inline(always)], #[cold] and the codegen-unit trap
Hard
239
Custom hashing: beating SipHash on integer keys
Hard
240
String and text performance, graded
Medium
241
Iterator performance: where the abstraction leaks
Medium
242
Const generics: moving a size from runtime to compile time
Hard
243
const fn and compile-time evaluation
Hard
244
Threads, spawn and join
Easy
245
move closures: why the compiler forces your hand
Easy
246
JoinHandle, panics and thread::Result
Medium
247
Scoped threads: borrow, do not clone
Easy
248
Balanced ranges: the arithmetic before the threads
Easy
249
Send: what it actually guarantees
Medium
250
Sync: T is Sync exactly when &T is Send
Medium
251
unsafe impl Send, and the disjoint-capture trap
Hard
252
Arc: shared ownership across threads
Easy
253
Arc gives you sharing, not mutability
Easy
254
Mutex and MutexGuard: you cannot forget to unlock
Easy
255
Guard lifetimes: still holding the lock, still not meaning to
Hard
256
Poisoning: what a panic does to a lock
Medium
257
Index tagging: the pattern that makes concurrency testable
Medium
258
Deadlock by lock ordering: the guarantee Rust does not give you
Hard
259
mpsc channels: message passing basics
Easy
260
The drop(tx) hang: a bug with no diagnostics
Medium
261
sync_channel: bounded buffers and backpressure
Medium
262
Condvar: a wakeup is a hint, the predicate is the truth
Hard
263
Safe parallel mutation with chunks_mut
Medium
264
Barrier: keeping workers in lock step
Medium
265
park and unpark: building a blocking primitive by hand
Hard
266
Atomics: counters, flags and fetch_*
Medium
267
CAS loops: compare_exchange and fetch_update
Hard
268
A store-buffer memory-model interpreter
Hard
269
A single-producer single-consumer ring buffer
Hard
270
OnceLock: one initialisation, however many threads race for it
Medium
271
static mut and the edition-2024 wall
Medium
272
Build a spinlock from AtomicBool and UnsafeCell
Hard
273
Rc<RefCell<T>> vs Arc<Mutex<T>>, and the arena that beats both
Medium
274
Futures are lazy: nothing happens until you poll
Easy
275
The `Future` trait: `Output`, `Poll`, and your first hand-written future
Easy
276
`block_on`: the smallest thing that can run a future
Easy
277
Countdown futures and the shape of `Pending`
Easy
278
Implementing `Wake`: a real task waker and the lost-wakeup bug
Medium
279
`RawWaker` and `RawWakerVTable`: building a `Waker` with unsafe
Hard
280
`Unpin` in practice: making `Pin<&mut Self>` usable without unsafe
Medium
281
Hand-compile an `async fn` into a state machine
Hard
282
`poll_fn`: a future from a closure
Easy
283
Hand-rolling `join`: running two futures concurrently
Medium
284
Hand-rolling `select`: racing futures and dropping the loser
Medium
285
Structural pinning and safe pin projection by hand
Hard
286
The minimal single-threaded executor
Hard
287
Spawning, `JoinHandle`, and getting values back out
Hard
288
A logical-time reactor: timers without a clock
Hard
289
Fairness and starvation in your scheduler
Medium
290
Cancellation is just: stop polling and drop
Medium
291
Cancel safety, blocking, and locks across `.await`
Hard
292
Async recursion, async closures, and streams
Hard
293
The five superpowers — and the four myths
Easy
294
`unsafe {}` vs `unsafe fn`: edition 2024 splits the two meanings
Easy
295
Safety comments as a discipline: `// SAFETY:` and `# Safety`
Easy
296
Raw pointers: `*const T` and `*mut T`
Easy
297
`&raw const` / `&raw mut`: a pointer without a reference
Medium
298
Pointer arithmetic: `offset`, `add`, `sub`, `wrapping_*`
Medium
299
`ptr::read`, `write`, `copy`, `copy_nonoverlapping`, `drop_in_place`
Medium
300
`NonNull<T>`, null, and dangling-but-aligned
Medium
301
Provenance: a pointer is not an integer
Hard
302
Validity invariants vs safety invariants
Medium
303
`UnsafeCell`: the only legal path to interior mutability
Medium
304
Implementing `Vec`: layout, allocation, push, pop
Hard
305
Implementing `Vec` part 2: `insert`, `remove`, `Deref`, `IntoIter`
Hard
306
Implementing a reference-counted pointer
Hard
307
`PhantomData`, variance and drop-check
Hard
308
Leaking is safe, and exception safety
Hard
309
Splitting borrows: implement `split_at_mut`
Medium
310
`union`, `transmute` and `MaybeUninit`
Hard
311
Partial initialisation and leak-free unwinding: a `MaybeUninit` ring buffer
Hard
312
Your first macro_rules!: matchers and transcribers
Easy
313
Fragment specifiers: the complete tour
Medium
314
expr is atomic, tt is not: the precedence trap
Medium
315
Repetition: $(...),* and friends
Easy
316
Nested repetition and zipping metavariables
Hard
317
Counting repetitions without ${count} (which is still unstable)
Medium
318
Macro recursion and incremental TT munchers
Hard
319
Internal rules and push-down accumulation
Hard
320
Callbacks and TT bundling
Hard
321
Macro hygiene: why your variable didn't leak
Medium
322
Follow-set rules: why the compiler rejects your matcher
Hard
323
Building std's macros: vec!, matches!, a HashMap literal
Medium
324
A mini DSL: compile-time expression evaluation
Hard
325
Calling C from Rust: libc is already linked
Medium
326
Edition 2024: unsafe extern blocks and safe declarations
Medium
327
Edition 2024: #[unsafe(no_mangle)] and calling Rust from C in one file
Hard
328
C strings, ownership across the boundary, and FFI-safe types
Medium
329
Nullable pointer optimization, callbacks and qsort
Hard
330
Opaque pointers, variadics, and the FFI hazard catalogue
Hard
331
Type-driven design: newtypes, smart constructors, builders, typestate
Hard
332
Enum state machines with data-carrying transitions
Hard
333
Folding an AST: separating traversal from action
Hard
334
A macro-generated FFI binding layer
Hard
335
Build Rc<T> and RefCell<T> from scratch
Hard
336
Capstone: an ownership-only job scheduler
Hard
337
Capstone: build a thread pool
Hard
338
Capstone: an async executor with a logical-time reactor
Hard
339
Capstone: a fully-typed fallible parser
Hard
340
Capstone: audit and repair an unsound abstraction
Hard
341
Unsound API smells: the patterns that are always wrong
Medium
342
Designing safe wrappers around unsafe FFI
Hard