We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
Rust explainers
RustLong-form theory: intuitions, derivations, and modern variants. Each article has questions sprinkled throughout. Click to reveal the answer when you've thought about it.
-
What
clippy -D warningsactually checksClippy is a graded gate here, so you should know what the gate is — the six lint groups, which are on by default, why the most useful ones are off, and the file that passes clippy while being obviously wrong.
-
Comments, doc comments, and how to read docs.rs
Rust's documentation story is one of its genuinely best ideas: doc comments are Markdown, they compile to a website, and their examples run as tests. Knowing how to read the output is the difference between needing help and not.
-
Rust for X programmers: which wall you are about to hit
Rust's difficulty is not uniform — it depends almost entirely on which language you are arriving from. Four origin stories, the specific wall each one hits, and the three big walls everybody hits eventually.
-
String vs &str, part 1: the memory model
The most confusing early topic in Rust, and the reason is that people try to memorise conversion incantations before they have the picture. Here is the picture — and it is the same picture as Vec and slices, which you already have.
-
Copy and Drop are mutually exclusive
The moment Copy stops being a list of primitives to memorise and becomes a theorem you can apply to any type: Copy means no destructor, and all fields Copy. Two compiler errors, E0184 and E0204, are the proof.
-
"Just clone it": the honest argument
Both sides of the community's most repeated argument, in their strongest form — and the list of counter-moves you now own, so that "clone it" is a choice rather than a reflex or a taboo.
-
Aliasing XOR mutability: the one rule
The single axiom the whole borrow checker is a corollary of — stated precisely, in terms of places rather than variables, checked statically over every path.
-
Why the rule exists: aliasing, noalias, and the optimizer
The answer to "why can't I have two &mut, I promise I'll be careful" — the guarantee has already been spent by codegen, so being careful is not a substitute.
-
The borrow checker is a proof system, not a linter
The synthesis of Track 3: four defended claims about what the borrow checker actually is, why every rejection is exactly one of three things, and why clippy is a fundamentally different kind of tool.
-
#[non_exhaustive]: designing enums you can extend later
The semver-facing half of enum design. Why std::io::ErrorKind forces you to write a wildcard, what that costs every downstream matcher forever, and why this one is an article: the attribute is a verified no-op inside the crate that defines it.
-
Struct-of-enums vs enum-of-structs: choosing the axis of variation
The same domain modelled three ways, with the trade-offs measured: variant count, size_of, number of match sites, and the cost of adding a case. Where the exhaustiveness benefit stops paying for itself.
-
Capacity, Amortisation, and Why Rust Collections Never Shrink
Length is how many elements you have; capacity is how many you have room for. Where the difference costs you allocations, why the wall-clock win is smaller than everyone expects, and why
clear()gives back nothing. -
Choosing the Container: The std Complexity Table, Annotated
Vec, VecDeque, LinkedList, HashMap, BTreeMap, HashSet, BTreeSet, BinaryHeap — what each one is really for, with the asymptotics and the measurements that contradict them.
-
No null, no exceptions: Option and Result are just enums
The mental-model reset that makes the rest of Rust's error handling obvious: Option is not null, Result is not a checked exception, and neither one is a language feature.
-
The
?operator, exactly?is areturnplus aFrom::from. Knowing the desugaring explains every error message you will ever get from it, including the confusing associated-type one. -
When panic! is correct, and when it is a bug
A crisp rule for the judgement call that separates competent error handling from expert error handling — plus an honest account of where the Rust community disagrees.
-
Error enums vs Box<dyn Error>
The design decision that is semver-visible and expensive to get wrong — with measured numbers that correct the folklore in both directions.
-
What thiserror and anyhow actually generate
A line-by-line translation from the two crates you will meet on day one of any Rust job back to the hand-written code you already wrote in this track.
-
The comparison traits, read as a design
PartialEq, Eq, PartialOrd, Ord and Hash are not five unrelated derives — they are a layered contract, half of which the compiler enforces and half of which it merely trusts you about.
-
How
x.foo()actually resolvesEvery confusing 'method not found' and every E0034 in this course comes from one small algorithm. It is invisible until it misfires — so here it is, written out, with the classic
.borrow()trap worked through. -
Traits with no methods: Copy, Sized, Send, Sync
Four traits you can never call. They carry no behaviour at all — only facts the compiler uses to decide what your code is allowed to do, and they explain a whole family of otherwise baffling errors.
-
#[derive] lies about its bounds
The derive macros add a bound on every type parameter, whether or not the impl needs it. The error lands at the use site rather than at the derive, which makes it genuinely hard to debug — until you know the rule.
-
Every reference already has a lifetime (you just haven't seen one)
Lifetimes are not a new feature you are about to switch on. They have been in every reference you have written since chapter one — the compiler was just filling them in for you. This is the vocabulary you need before any of the syntax makes sense.
-
A lifetime is a constraint, not a duration
Every wrong model of lifetimes reduces to "the annotation controls how long something lives". Replace it with the region model — a lifetime is a set of program points, and
'a: 'bmeans one set contains the other — and the rest of the topic stops being mysterious. -
How to read a lifetime error
A decoder ring for the eight diagnostics you will actually meet — E0106, E0515, E0597, E0716, E0621, E0505, E0499, E0502 — each with a reproducer verified on rustc 1.95, plus the honest news that the hardest failures carry no error code at all.
-
The three elision rules, precisely
The rule rustc actually implements — which is not the one in the Book, and not quite the one in the Reference either. Both err in the loose direction, and this article shows the counterexamples that prove it, verified on rustc 1.95.
-
When elision fails: reading E0106
E0106 is the first hard wall almost everyone hits, and the natural response — paste
'auntil it compiles — is the wrong one. It is a question with a right answer that depends on your intent. Here is every shape it comes in, and how the body decides. -
Returning a reference to a local — the E0515 wall
The error where people conclude Rust won't let them write functions. Naming the exact impossibility — the caller picks
'a, so no local can satisfy it — dissolves it, and the four real fixes are a genuine API-design lesson. -
Structs that hold references
The second stall point after E0106: you add
<'a>to a struct, watch it metastasise through your whole program, and conclude Rust cannot express real data structures. Here is the framing that stops the spread — a lifetime-parameterised struct is a view, not a container. -
Elision rule 3 and the
&selftrapOne of the most important ideas in the language, and it appears in almost no beginner material: elision rule 3 will hand you a signature that compiles, passes every test, and is wrong. The diagnostic question that tells the two cases apart, and why the compiler cannot help you here.
-
Hidden, elided, named: the
'_rule andmismatched_lifetime_syntaxesThe one lifetime-adjacent lint that will fail your build without you having written a single
'a. Three syntax groups, one consistency rule, and why "I didn't use lifetimes and it's yelling about lifetimes" is exactly what it looks like. -
'statichas two completely different meaningsT: 'staticis the single most misread bound in Rust. It does not mean "lives forever" — it means "does not borrow anything short-lived", and every owned type satisfies it. Sorting this out unblocks threads, trait objects and error handling in one go. -
Trait objects have lifetimes too
The most surprising set of implicit rules in the language: a lifetime you never wrote, defaulting to
'static, silently rejecting your program. You hit it the moment you box a closure or return aBox<dyn Error>. -
Closures don't follow function elision
You have just internalised the elision rules. Then you write the same function as a closure and it stops compiling, with no error code. This is a genuine compiler limitation, not a rule you misunderstood — here is what actually works on 1.95, tested.
-
Temporary scopes: E0716 and what edition 2024 changed
The last piece needed to fully predict when any value dies — and because this site compiles with
--edition 2024, the two changed rules are live here while most tutorials on the internet are on the other side of them. -
Outlives bounds:
'a: 'b,T: 'a, and implied boundsOnce you write generic code that holds references, these bounds appear in every error message. Without the vocabulary they are noise; with it,
T: 'aerrors become readable. This is the formal grammar, and the prerequisite for everything above it. -
Disjoint field capture: what changed in edition 2021
The most-used Rust feature nobody knows the name of. Since RFC 2229 a closure captures precise places, not whole variables — which silently deleted a whole category of borrow errors, and quietly changed when your values drop.
-
When collect() is a mistake
collect() is the first tool everyone reaches for and the easiest to over-use. Four anti-patterns, the honest counter-argument for each, and why the lint you would expect to catch this is switched off.
-
Iterator internals: why for and fold are not the same loop
External versus internal iteration, why std overrides fold on Chain and Flatten, what try_fold is really for, and an honest answer to "are iterators as fast as loops".
-
Crate vs package vs workspace vs module
Four words that get used interchangeably and mean four different things. Getting them straight answers "why can't I use this from my binary", "what does pub(crate) actually cover", and "why did adding a file do nothing".
-
Cargo.tomlessentialsThe manifest is the interface between your code and the whole ecosystem, and most of its fields are never explained. Including the one whose default is genuinely hostile.
-
Workspaces, publishing, MSRV and docs.rs
Every non-trivial project ends up a workspace, and publishing is where the version, feature and SemVer rules are finally enforced — partly by tooling, mostly by convention.
-
#[test], the assert macros, and what a test actually isTesting is a language feature in Rust, not a library convention — which is why there is no runner to configure and why tests can see your private functions. Also: why this site cannot grade
#[test]at all. -
Unit tests,
#[cfg(test)], integration tests and the lib+bin split"How do I test private functions" has an answer in Rust that costs nothing — and it only makes sense once you know that private means "this module and its descendants". Plus the single most cited Cargo testing gotcha.
-
Enum layout and niche optimization: why
Option<Box<T>>is freeOption<Box<T>> is exactly as big as Box<T>, and Option<i32> is twice as big as i32. Both facts come from one mechanism — the niche — and knowing it is the difference between trusting "zero cost" and verifying it.
-
Shared does not mean immutable
Interior mutability looks like an escape hatch that turns off the borrow checker. It is not. The check moved to runtime, it will panic, and the whole family makes sense once you rename the two reference types.
-
Derefis for smart pointers, and nothing elseEvery type in this track — Box, Rc, Ref, RefMut — implements Deref, and that single trait is why they feel like the values they hold. It is also the most abused trait in Rust, and the rule for when to implement it is one sentence long.
-
Choosing a smart pointer
Nine types, one decision table, and the honest headline: most code needs none of them. Plus the cost ordering in real numbers, the nesting rules, and where the community genuinely disagrees.
-
Choosing between generics, impl Trait, &dyn, Box<dyn> and enums
Five ways to say "something that implements this trait", what each costs, and the decision procedure — including the enum option almost nobody teaches.
-
Monomorphisation: what generics compile to, and what that costs
The mechanism behind every "zero-cost abstraction" claim, and the reason large Rust projects compile slowly — with an honest measurement and the outlining pattern almost nobody teaches.
-
async fn in traits and RPITIT: what works and what does not
Since 1.75 traits can return
impl Traitand declareasync fn. The hard limitation persists in 1.95: such traits are not dyn compatible — verified against this toolchain. -
Benchmark methodology: why your microbenchmark is lying to you
What the number on your screen is and is not — dead-code elimination, black_box's limits, constant folding, run-to-run variance, and the deepest problem of all.
-
Profiling Rust: finding the hot spot before you optimise it
Everything else in this track is a technique. This is how you find out which one to apply — the tools, the build settings that make them readable, and the discipline.
-
SIMD in Rust: what is stable, what is not
The accurate 2026 map of the three tiers — auto-vectorisation, core::arch intrinsics, and the portable_simd that is still nightly — and why tier one is where the wins are.
-
Choosing your concurrency primitive
You now know threads, scopes, channels, Arc, Mutex, RwLock, Condvar and Barrier. The hard part was never any one of them — it is knowing which one the problem in front of you actually wants, and which reflex to distrust.
-
Structuring shared state so it cannot deadlock
Deadlock is not a bug you fix, it is a property you design out. Six rules a reviewer can actually apply, and an honest account of the three tools std does not give you.
-
Memory orderings, honestly
The most over-claimed topic on the internet, and the one place in this course where no test can tell you whether you were right. Release/acquire, why SeqCst is a smell, and why you must reason about this instead of running it.
-
Send and Sync as unsafe auto traits
unsafe impl Send for X {}is a proof obligation, not an incantation for quietening the compiler. What the proof has to establish, where the bounds come from, and why MutexGuard is Sync but not Send. -
False sharing: correct code, ten times slower
Two counters that share nothing can still fight, because the hardware's unit of sharing is not the variable — it is the 64-byte cache line. Why this is real, why it can be a 10x effect, and why no test in this course can catch it.
-
What this grader cannot check, and what real teams use
A green tick here means "produced the right answer on this machine, this time". An honest inventory of the concurrency bugs that are invisible to this course's tooling, a scorecard for clippy, and the three tools that actually find them.
-
Why async exists: blocking, threads, and the cost of waiting
Async is not a performance button. It is a way to multiplex tens of thousands of mostly-idle waits onto a handful of threads — and Rust ships the syntax and the trait but deliberately no runtime.
-
WakerandContext: the wakeup contractThe three clauses every future must honour when it returns Pending — and why breaking clause two produces a program that hangs at 0% CPU with no error message anywhere.
-
What
async fnactually compiles toAn async fn is sugar for a function returning an opaque stackless coroutine — one state per suspension point, holding exactly the locals live across it. Half the mysteries of async Rust are consequences of that one picture.
-
PinandUnpin, motivatedPin is not compiler magic. It is a library type that refuses to hand out &mut T unless T: Unpin — and that single refusal is the entire mechanism protecting self-referential futures.
-
How tokio and smol build on these primitives
Every piece of the runtime you just built maps onto a production counterpart: the ready queue becomes a work-stealing scheduler, the logical clock becomes epoll plus a timer wheel, TaskWaker becomes a refcounted task header.
-
The function-colouring debate, honestly
Async functions can only be called from async contexts, which splits ecosystems and duplicates APIs. That critique is correct — and so are the counterarguments. The track ends with calibration rather than a verdict.
-
Meet Safe and Unsafe: what
unsafeis actually forThree words defined precisely — undefined behaviour, unsound, sound — plus the two things Rust does not consider unsafe at all, and the three misconceptions that produce every serious unsafe bug.
-
What is Undefined Behaviour? The complete list
The Rust Reference's catalogue of undefined behaviour, with a concrete example for each bullet, plus the invalid-value table in full — a lookup page, not a memorisation exercise.
-
Aliasing: why
&mutmeansnoaliasThe aliasing guarantee survives raw pointers,
UnsafeCellrelaxes exactly one half of it, and transmuting&Tto&mut Tis always undefined behaviour — no exceptions, and rustc says so by name. -
Stacked Borrows, Tree Borrows and Miri
The two candidate aliasing models, what Miri can and cannot detect, and the honest answer to "is this allowed?" — the rules are still being written, so the discipline is to stay well inside the conservative core.
-
Safety invariants and privacy: unsafe contaminates the module
Safety in Rust is non-local: an unsafe block's soundness depends on safe code elsewhere, so the unit you must audit is the whole module — and privacy is the only thing that keeps that number finite.
-
Why macros exist: code that writes code
The three things a function fundamentally cannot do, why macro_rules! is not textual substitution, and why the community reaches for generics first.
-
Debugging macros when expansion goes wrong
A seven-step procedure for macro errors: read the span, turn on meta_variable_misuse, stringify! what you captured, and force the compiler to print the expansion.
-
The real limits of macro_rules!
The eight things macro_rules! structurally cannot do, why each limit exists, what people do instead — and the rust-analyzer cost nobody warns you about.
-
Procedural macros: the three kinds
Function-like, derive and attribute macros, their exact signatures, and the bootstrapping reason a proc macro can never live in the crate it transforms.
-
How #[derive(Debug)] works, and attribute macros demystified
Derive appends and cannot modify; attribute macros replace the whole item. Traced end to end, including what #[tokio::main] actually rewrites your main into.
-
Function-like proc macros and proc-macro hygiene
Procedural macros have no hygiene at all, which is why generated code writes ::core::option::Option::None — plus the token model that makes tt intuitions fail to transfer.
-
Choosing: generics, macro_rules!, or a proc macro
The escalation ladder, what each rung really costs, and the live disagreement about whether proc macros outside derives belong in a codebase at all.
-
What an ABI is, and why FFI exists
API is a promise to a programmer, ABI is a promise to a machine. Why Rust's own ABI is deliberately unstable, and why extern "C" and #[repr(C)] are two independent things you almost always need both of.
-
Zero-cost abstractions, examined
What the phrase has always meant, the four places Rust genuinely does not deliver it, and the measurements from this course that vindicate it anyway.
-
The reviewer's idiom checklist
One screen of things to look at, in the order a reviewer looks at them — with the contested entries marked as contested and the ones no lint catches called out.
-
When not to use unsafe, and when not to use async
Every reason people reach for unsafe, and the safe answer to each; then the threads-versus-async decision — with both live community disagreements presented rather than resolved.