Rust Mastery¶
Ownership, async, unsafe, FFI, production architecture.
Printing this page
Use your browser's Print → Save as PDF. The print stylesheet hides navigation, comments, and other site chrome; pages break cleanly at section boundaries; advanced content stays included regardless of beginner-mode state.
Rust Mastery Blueprint-A 24-Week Master-Level Syllabus¶
Authoring lens: Senior Principal Systems Engineer / Rust Architect.
Target outcome: A graduate of this curriculum should be capable of (a) submitting non-trivial PRs against rust-lang/rust, (b) owning a low-latency fintech matching engine or risk pipeline, or (c) writing a kernel module in rust-for-linux without supervision.
This is not a "learn Rust in N days" track. It assumes the reader is already a working software engineer who can read C, has shipped production code in some language, and is willing to read source code (LLVM, Tokio, glibc, the Rust reference) as a primary learning surface.
Repository Layout¶
| File | Purpose |
|---|---|
00_PRELUDE_AND_PHILOSOPHY.md |
Why Rust; the affine type system; the cost model; reading list. |
01_MONTH_FOUNDATIONS.md |
Weeks 1–4. Toolchain, memory layout, ownership, error model. |
02_MONTH_TYPE_SYSTEM.md |
Weeks 5–8. Lifetimes, variance, traits, smart pointers, Drop checker. |
03_MONTH_CONCURRENCY_ASYNC.md |
Weeks 9–12. Atomics, lock-free, Pin/Unpin, Tokio & Smol internals. |
04_MONTH_UNSAFE_FFI_MACROS.md |
Weeks 13–16. unsafe, FFI, declarative & procedural macros. |
05_MONTH_PRODUCTION_ARCHITECTURE.md |
Weeks 17–20. Hexagonal, zero-copy I/O, observability, testing. |
06_MONTH_MASTERY_CAPSTONE.md |
Weeks 21–24. Custom data structures, no_std, rustc internals, capstone. |
APPENDIX_A_PRODUCTION_HARDENING.md |
LTO, PGO, BOLT, cargo-geiger, supply chain auditing. |
APPENDIX_B_DATA_STRUCTURES.md |
Build-from-scratch reference: B-Tree, lock-free hash map, MPSC, slab. |
APPENDIX_C_CONTRIBUTING_TO_RUSTC.md |
The compiler pipeline; bootstrap; MIR; first PR playbook. |
CAPSTONE_PROJECTS.md |
Three terminal projects, one per career track. |
How Each Week Is Structured¶
Every weekly module follows the same five-section format so the reader can budget time:
- Conceptual Core-the why, with a mental model.
- Mechanical Detail-the how, down to layout and ABI where relevant.
- Lab-a hands-on exercise that cannot be completed without internalizing the concept.
- Idiomatic & Clippy Drill-read 2–3 lints, refactor a sample to silence them, understand why each lint exists.
- Production Hardening Slice-an LTO/PGO/cross-compile/audit micro-task that compounds across weeks.
Each week is sized for ~12–16 focused hours. Skip the labs at your peril; the labs are the curriculum.
Progression Strategy¶
The phases form a dependency DAG, not a linear track:
Foundations ──► Type System ──► Concurrency ──► Unsafe / FFI / Macros
│ │ │ │
└──────────────┴────────┬───────┴───────────────────┘
▼
Production Architecture
│
▼
Mastery & Capstone
The Production Hardening slice is intentionally orthogonal-it accumulates a hardening/ workspace that, by week 24, is a publishable Cargo template.
Non-Goals¶
- This curriculum does not cover web frameworks (Axum/Actix) as primary subjects. They appear only as integration surfaces in Month 5.
- Game development, GUI, and WASM front-ends are out of scope. Pointers are given for the curious in
00_PRELUDE_AND_PHILOSOPHY.md. - "Rewrite it in Rust" advocacy is explicitly avoided; the reader should finish the program able to argue against using Rust when it is the wrong tool.
Capstone Tracks (pick one in Month 6)¶
- Compiler Track-land a non-trivial PR in
rust-lang/rust(e.g., a clippy lint, a diagnostic improvement, or a small MIR transform). - Fintech Track-implement a multi-asset limit-order-book matching engine with sub-microsecond p99 hot-path latency, fuzzed and verified under
loom. - Kernel Track-write a Rust character-device driver for
rust-for-linux, complete withKUnittests and a working out-of-tree build.
Details in CAPSTONE_PROJECTS.md.
Prelude-The Philosophy Behind the Syllabus¶
Before week 1, sit with this document for an evening. The rest of the curriculum is mechanically dense; this is the only chapter where we step back and define the shape of the discipline.
1. Rust Is an Affine Type System Bolted to a Region Calculus¶
Rust is most often described as "a memory-safe systems language." That description is marketing, not engineering. The accurate description:
- Affine types: every value can be used at most once by move (
std::mem::take - style semantics), unless it isCopy`. - Region inference: lifetimes (
'a) are region variables in a constraint solver-they exist only at compile time and never appear in the binary. - Sub-structural extensions: traits like
Send,Sync,Unpin,Sizedadd capability axes that the type checker propagates.
If you internalize this framing, you stop fighting the borrow checker and start reading the constraint failures it emits.
Reading: Ralf Jung et al., RustBelt: Securing the Foundations of the Rust Programming Language (POPL 2018). Skim the introduction; you do not yet need the separation-logic semantics.
2. The Cost Model You Must Adopt¶
A working Rust engineer reasons about every line of code along four axes simultaneously:
| Axis | Question to ask |
|---|---|
| Ownership | Who frees this? When? |
| Layout | Where does this live-stack, heap, .data, .rodata, TLS? Aligned to what? |
| Codegen | Is this monomorphized? How many copies will the linker see? |
| Failure | What does the panic path look like? Is unwinding allowed here? |
Beginner courses teach axis 1 only. This curriculum forces all four into your hands by week 8.
3. The Reading List¶
These are referenced throughout the curriculum. You are not expected to read them cover-to-cover before starting; they are pinned tabs.
Primary
- The Rust Reference-doc.rust-lang.org/reference. The normative spec.
- The Rustonomicon-doc.rust-lang.org/nomicon. Unsafe semantics.
- Rust for Rustaceans (Jon Gjengset)-the only book worth reading after The Book.
- Programming Rust, 2e (Blandy/Orendorff/Tindall)-best treatment of the type system.
Secondary, by phase
- Concurrency: Rust Atomics and Locks (Mara Bos). Read once at week 9, again at week 11.
- Async: Tokio's runtime/ source tree; withoutboats's blog (without.boats).
- Macros: The Little Book of Rust Macros and dtolnay/proc-macro-workshop.
- Compiler: rustc-dev-guide (rustc-dev-guide.rust-lang.org).
- Allocators: Phil Opperman's Writing an OS in Rust-chapters 9–11.
Adjacent canon (not Rust, but you must know it) - Drepper, What Every Programmer Should Know About Memory (2007). Re-read in week 9. - Herlihy & Shavit, The Art of Multiprocessor Programming, chapters 7, 9, 13. - Intel SDM Vol. 3A, chapter 8 (memory ordering). RISC-V and ARMv8 equivalents if those are your targets.
4. Curriculum Philosophy: "Read the Source, Ship the Lab"¶
Three rules govern every module:
- Source first, blog second. When the curriculum says "study Tokio's
Notify," it means opentokio/src/sync/notify.rsand read it. Blogs go stale; commits are dated. - One lab per concept, one PR per phase. By the end of each month, the reader has produced one open-source-quality artifact (crate, gist, or PR)-not a notebook of toy snippets.
- The compiler is the teacher. When you do not understand why something fails to compile, the first response is to enable
RUSTC_LOG=trace, the second is to consultrustc --explain Exxxx, and only the third is to ask another human.
5. What Rust Is Not For¶
A graduate of this curriculum should be able to argue these points in a design review without sounding ideological:
- Greenfield CRUD web apps with frequent schema churn. Your iteration speed will be dominated by compile times and
serdeceremony. Go, TypeScript, or Elixir are usually better. - Throwaway scripts. Use Python.
- Code where the team has no C/C++/systems intuition. Rust does not erase complexity; it surfaces it. A team that has never debugged a use-after-free will struggle to read
Pin<&mut Self>. - GUI applications with rich tooling demands. The ecosystem is improving but is still a step behind Qt/SwiftUI/Flutter.
The signal that Rust is the right tool: you have a memory-safety, latency-tail, or single-binary-deployment constraint that ranks above developer iteration speed.
6. A Note on AI-Assisted Workflows¶
Modern Rust authors use LLM tooling. Two rules:
- Never paste async/
unsafecode from a model without reading the generatedMIR(cargo rustc -- --emit=mir). The failure modes are subtle and the model's training data is biased toward older, simpler patterns. - Macros are the worst LLM modality. Hygiene bugs and span errors slip past human review. Write proc macros yourself; use the model only for the boilerplate around
syn::parse.
You are now ready for Week 1. Open 01_MONTH_FOUNDATIONS.md.
Month 1-Foundations: Toolchain, Memory Layout, Ownership, Errors¶
Goal: by the end of week 4 you can (a) describe where every value in a non-trivial program lives in memory, (b) read a borrow-checker error and locate the conflicting region without trial and error, (c) explain why ? is not exception handling, and (d) ship a small CLI as a statically linked binary with reproducible builds.
Weeks¶
- Week 1 - The Toolchain and the Compiler Pipeline
- Week 2 - Memory Layout: Stack, Heap, Data, BSS, TLS
- Week 3 - Ownership, Borrowing, and Region Inference
- Week 4 - The Error Model
Week 1 - The Toolchain and the Compiler Pipeline¶
1.1 Conceptual Core¶
rustupis a toolchain multiplexer, not a compiler. Understand the channel model (stable,beta,nightly, dated nightlies), components (rust-src,rust-analyzer,miri,rustc-dev), and target triples.cargois a build orchestrator on top ofrustc. It is not the compiler. Almost every "cargo problem" is arustcinvocation problem in disguise. Learn to print the actualrustccommand withcargo build -vv.- The compilation pipeline: source → lexer → parser → AST → HIR (high-level IR) → THIR → MIR (mid-level IR, where borrow checking runs) → LLVM IR → object code → linker. You will revisit this in Month 6; this week you only need to name the stages.
1.2 Mechanical Detail¶
rustc --print=cfg,rustc --print=target-list,rustc --print=sysroot. Learn these by muscle memory.- The
Cargo.lockdiscipline: committed for binaries, gitignored for libraries-and why (the dependency-resolver contract). cargo tree -dfor duplicate dependency detection;cargo metadata --format-version=1 | jqfor programmatic inspection.- The role of
build.rs: when it runs, what it can emit (cargo:rustc-link-lib,cargo:rerun-if-changed), and why it is a frequent supply-chain attack vector.
1.3 Lab-"Hello World, Audited"¶
- Create
hello-audited. Pin a specific stable toolchain viarust-toolchain.toml. - Build with - -release
. Runobjdump -h target/release/hello-auditedand identify the.text,.rodata,.data,.bss, and.eh_frame` sections. - Strip with
strip -sand compare binary sizes. Now rebuild withRUSTFLAGS="-C strip=symbols -C panic=abort"and compare again. - Document the size delta from each flag in
NOTES.md. You should observe.eh_frameshrinking dramatically when panic=abort is set-explain why.
1.4 Idiomatic & Clippy Drill¶
- Enable
#![deny(clippy::pedantic, clippy::nursery)]in your crate root. Half of the lints will fire on idiomatic-looking code. Read each one's rationale on the clippy lint index-not just the fix.
1.5 Production Hardening Slice¶
- Add a
.cargo/config.tomlthat sets[profile.release] lto = "fat"andcodegen-units = 1. Rebuild. Note compile-time cost vs. binary-size win. - Set up
cargo-denywith a baselinedeny.toml(license allowlist, advisory database). This file will grow each week.
Week 2 - Memory Layout: Stack, Heap, Data, BSS, TLS¶
2.1 Conceptual Core¶
- A Rust value lives in exactly one of: a stack frame, the heap (via an allocator),
.data(mutable static),.rodata(immutable static /const),.bss(zero-initialized static), or thread-local storage. let x = 5;putsxon the stack.Box::new(5)puts the integer on the heap and a pointer on the stack.static X: i32 = 5;putsXin.rodata.static mut Y: i32 = 0;putsYin.data. Internalize this taxonomy before writing another line of code.constvsstatic:constis inlined at every use site (no address);statichas a stable address. The distinction matters for FFI and for sharing across threads.
2.2 Mechanical Detail¶
std::mem::size_of,align_of,size_of_val,align_of_val. Run them on(),bool,&u8,&[u8],&dyn Debug,Box<dyn Debug>,Option<&T>,Option<Box<T>>. Predict the answers, then check.- Niche optimization:
Option<&T>is 8 bytes, not 16, because the null pointer is the niche.Option<Box<T>>likewise. Learn to spot when you've broken the niche by adding a redundant variant. #[repr(C)],#[repr(transparent)],#[repr(packed)],#[repr(align(N))]: when each is appropriate. Default#[repr(Rust)]reorders fields for tightest packing-this is observable via - -print=type-sizes` (nightly).- Thread-local storage:
thread_local!macro, the OS-level cost (__tls_get_addron Linux glibc), and why it is rarely the right answer.
2.3 Lab-"Layout Forensics"¶
Build a binary that allocates one value of each "kind":
- a stack [u8; 64],
- a Box<[u8; 64]>,
- a static FOO: [u8; 64] = [0xAB; 64];,
- a static mut BAR: [u8; 64] = [0; 64];,
- a thread_local! RefCell<[u8; 64]>.
Print the address of each (&value as *const _ as usize). Run under cat /proc/self/maps (spawn cat from inside the program) and prove which segment each address falls in. Write up the mapping in NOTES.md.
2.4 Idiomatic & Clippy Drill¶
- Lints to study:
clippy::large_stack_arrays,clippy::large_enum_variant,clippy::box_collection,clippy::vec_box. Each maps to a layout pathology.
2.5 Production Hardening Slice¶
- Set
RUSTFLAGS="-C link-arg=-Wl,--print-memory-usage"(or usecargo-bloat) to inspect per-section binary footprint. Addcargo bloat --release --cratesoutput toNOTES.md. This is the baseline for size-budget conversations later.
Week 3 - Ownership, Borrowing, and Region Inference¶
3.1 Conceptual Core¶
- Ownership is destructor scheduling: the owner is the entity that will run
Drop::drop. There is exactly one. - Borrowing is temporary capability delegation:
&Tgrants read capability,&mut Tgrants exclusive read+write capability. A capability cannot outlive the resource that backs it (the lifetime constraint). - Lifetimes are not durations. They are region variables that the compiler infers under a system of inequality constraints (
'a: 'bmeans region'aoutlives region'b). The compiler does not know "how long" anything lives in seconds-only the partial order of regions.
3.2 Mechanical Detail¶
- The three borrow-checker rules, stated formally:
- At any program point, for any place
p: at most one&mut por any number of&p, never both. - References must be valid for their entire region.
- The owner cannot mutate or move the value while a borrow is active (this is what NLL-non-lexical lifetimes-relaxed).
- Two-phase borrows (
v.push(v.len())): why this compiles even though it looks like aliasing. - Reborrowing:
&mut *rproduces a fresh&mutwith a shorter lifetime. This is the foundation for passing&mutreferences into functions repeatedly.
3.3 Lab-"Defeat the Borrow Checker, Then Submit"¶
You will be given (as exercise files) ten programs that the borrow checker rejects. For each:
1. Predict which rule is violated before reading the diagnostic.
2. Fix it three different ways (e.g., scope shrinking, split borrow, Cell/RefCell).
3. Pick the idiomatic fix and justify it in a one-line comment-but only if the comment captures non-obvious reasoning. (See feedback rule on comments.)
3.4 Idiomatic & Clippy Drill¶
clippy::needless_lifetimes,clippy::redundant_clone,clippy::ptr_arg. The first two are about elision; the third is about API ergonomics.
3.5 Production Hardening Slice¶
- Run
cargo clippy --workspace --all-targets -- -D warningsin CI from week 1 forward. This is non-negotiable.
Week 4 - The Error Model¶
4.1 Conceptual Core¶
- Rust has two error mechanisms, not one:
Result<T, E>-recoverable errors, encoded in the type system.- Panics-unrecoverable, stack-unwinding (or aborting) bugs.
?is not exception handling. It is sugar formatchplusFrom::fromon the error variant. Internalize this; it is the difference between a clean error model and a re-implementation of Java exceptions.panic = "abort"vspanic = "unwind": chooseabortfor binaries that own their process,unwindfor libraries that may be embedded in a host that wants to catch (e.g., a Python extension).
4.2 Mechanical Detail¶
- The shape of an idiomatic library error: an enum with
#[non_exhaustive],thiserror::Errorfor derivation, andFromimpls for upstream errors. - The shape of an idiomatic application error:
anyhow::Resultat boundaries, typed errors internally. The split is deliberate: libraries owe their callers structured errors; applications owe their operators readable context. Result::map_errand? - chain ergonomics. The anti-pattern ofunwrap()outsidemain/tests/build.rs`.#[track_caller]-the attribute that makes panic locations attribute to the caller rather than the panicking function. Why every helper that may panic should carry it.
4.3 Lab-"A Library With Two Faces"¶
Build parse-units: a small crate that parses strings like "3.5 GiB" into a structured Quantity. Requirements:
- Public API returns Result<Quantity, ParseError> where ParseError is a thiserror enum with at least four variants.
- Internally, use ? to compose. No unwrap allowed except in unit tests.
- Provide a binary parse-units-cli that uses anyhow and prints rich context with .with_context(|| ...).
- Ship 100% line coverage measured by cargo-llvm-cov.
4.4 Idiomatic & Clippy Drill¶
clippy::result_large_err(errors >128 bytes hurt the happy path),clippy::map_err_ignore,clippy::question_mark,clippy::unwrap_used,clippy::expect_used. Enable the last two asdenyin libraries.
4.5 Production Hardening Slice¶
- Add a
panic = "abort"release profile and apanic = "unwind"test profile. Confirm the binary shrinks under abort. AddRUST_BACKTRACE=1to your dev shell. - Wire up
cargo auditandcargo deny checkto CI. Both must pass on green main.
Month 1 Capstone Deliverable¶
A workspace foundations/ with three crates:
1. parse-units (week 4 lab) as a publishable library.
2. parse-units-cli as the application binary.
3. layout-forensics (week 2 lab) as an internal-only tool.
CI must run: cargo fmt --check, cargo clippy -D warnings, cargo test, cargo llvm-cov, cargo deny check, cargo audit. The workspace's release profile must enable lto = "fat", codegen-units = 1, panic = "abort", strip = "symbols". Document the resulting binary size in the workspace README.
Month 2-The Type System: Lifetimes, Traits, Smart Pointers, Drop¶
Goal: by the end of week 8 you can (a) write a function with higher-ranked trait bounds and explain why the bound is HRT, (b) predict from a type signature whether Box<T> or Rc<RefCell<T>> is appropriate, (c) design a public API that uses sealed traits to forbid downstream implementors, and (d) read and reason about variance.
Weeks¶
- Week 5 - Advanced Lifetimes, Variance, and HRTBs
- Week 6 - Traits, Coherence, and Monomorphization
- Week 7 - Smart Pointers and Interior Mutability
- Week 8 - Drop, the Drop Checker, and Destructor Discipline
Week 5 - Advanced Lifetimes, Variance, and HRTBs¶
5.1 Conceptual Core¶
- Subtyping in Rust exists only among lifetimes.
'static <: 'afor any'a. There is no Liskov-style subtyping for nominal types. - Variance describes how the subtyping of a generic parameter lifts to the type constructor:
&'a Tis covariant in'aandT.&'a mut Tis covariant in'a, invariant inT.fn(T) -> Uis contravariant inT, covariant inU.Cell<T>,*mut T,UnsafeCell<T>are invariant inT.- Higher-Ranked Trait Bounds (HRTBs):
for<'a> F: Fn(&'a str) -> &'a str - thefor<'a>quantifier means "for *every*'a` the caller might pick." Used pervasively in async closures and iterator combinators.
5.2 Mechanical Detail¶
- The lifetime-elision rules (three of them; memorize). Then write a function that elision cannot solve and observe the error.
PhantomData<T>: how to force a type to "act as if" it owned aTfor variance and Drop-checker purposes, even though noTis stored. Crucial for FFI wrappers and arena types.'staticis not "lives forever"-it means "could live until program end if it wanted to." A&'static stris an immutable reference whose region has no upper bound.- GATs (Generic Associated Types):
type Item<'a> where Self: 'a;. The pattern that finally unblocked lending iterators. Read RFC 1598 and the stabilization PR.
5.3 Lab-"A Lending Iterator"¶
Implement a WindowsMut lending iterator that yields overlapping &mut [T] windows over a slice. This requires GATs. Property-test it against a naive O(n²) reference implementation.
5.4 Idiomatic & Clippy Drill¶
clippy::needless_lifetimes,clippy::extra_unused_lifetimes,clippy::elidable_lifetime_names. Read RFC 2115 ("argument position impl Trait") and refactor a generic function to use APIT where the lifetime adds no information.
5.5 Production Hardening Slice¶
- Add
cargo-semver-checksto CI. Adding/removing a lifetime parameter to a public type is a SemVer-major change; the tool will catch it.
Week 6 - Traits, Coherence, and Monomorphization¶
6.1 Conceptual Core¶
- A trait is a named set of capabilities. Implementing a trait for a type is a claim that the type satisfies a contract-the contract is partly enforced by the compiler and partly by the implementor's discipline (e.g.,
HashandEqconsistency). - Coherence / orphan rule: an
impl Trait for Typeis allowed only if eitherTraitorTypeis local to the current crate. This is what prevents "two crates impl the same trait for the same type" diamond conflicts. - Monomorphization: every distinct type substitution at a generic call site produces a fresh compiled function in the binary.
Vec<u8>::pushandVec<i32>::pushare two different symbols. This is the dual edge of the language: zero-cost abstraction in exchange for binary bloat and compile time.
6.2 Mechanical Detail¶
- Static dispatch (
impl Trait, generics) vs dynamic dispatch (dyn Trait). The vtable layout ofdyn Trait: a fat pointer (data pointer + vtable pointer), where the vtable contains the destructor, size, alignment, and the trait method pointers. Inspect a real one withcargo asm. - Object safety / dyn compatibility: a trait is dyn-compatible if every method has a
Self: Sizedbound or takesselfby reference and does not returnSelfor useSelfin generic position. The 2024 edition formalized "dyn-compatible" terminology-use it. - Sealed traits: declare a private supertrait in a private module to prevent downstream impls. Used in
std::error::Errorhistorically, intokio::io::AsyncReadextensions, and in any API where future-compatibility demands closed extension. - Auto traits (
Send,Sync,Unpin,UnwindSafe,RefUnwindSafe): structural, opt-out via negative impl on nightly orPhantomData<*const ()>on stable.
6.3 Lab-"Bloat Forensics"¶
- Write a generic function
fn process<T: Display>(items: &[T]) -> Stringthat formats and concatenates. Instantiate it with five distinct types in a binary. - Run
cargo bloat --release --filter processand confirm there are five symbols. - Refactor to a
dyn Displayversion (&[&dyn Display]). Re-runcargo bloat. Document the binary-size delta and the codegen tradeoff. - Now read the disassembly of the dyn version with
cargo asmand identify the indirect call.
6.4 Idiomatic & Clippy Drill¶
clippy::needless_pass_by_value,clippy::trait_duplication_in_bounds,clippy::implicit_hasher. The last is subtle-explain why leakingRandomStateinto a public API is a SemVer hazard.
6.5 Production Hardening Slice¶
- Configure
[profile.release.package."*"] codegen-units = 1for maximum cross-function inlining, while keeping[profile.dev] codegen-units = 256for fast iteration. Document the build-time delta.
Week 7 - Smart Pointers and Interior Mutability¶
7.1 Conceptual Core¶
- A "smart pointer" in Rust is a value that owns a heap allocation and customizes Drop. The std hierarchy:
Box<T>-unique ownership, single heap allocation.Rc<T>-shared ownership, single-threaded, refcount.Arc<T>-shared ownership, atomic refcount, thread-safe.Weak<T>(paired withRc/Arc)-non-owning observer; breaks cycles.- Interior mutability is the controlled violation of "no
&mutwhile&exists." The valid mechanisms: Cell<T>-get/set by value, no references handed out, single-threaded, zero overhead.RefCell<T>-runtime borrow checking, panics on conflict, single-threaded.Mutex<T>/RwLock<T>-runtime borrow checking with thread blocking, multi-threaded.OnceCell<T>/OnceLock<T>-write-once, then immutable.UnsafeCell<T>-the only primitive that legally permits interior mutability; everything above is built atop it.
7.2 Mechanical Detail¶
UnsafeCell<T>is the one type in the language for which&UnsafeCell<T>may be cast to*mut Twithout UB, provided the contents are not aliased mutably. Read the Rustonomicon chapter; it is the keystone.- The memory layout of
Rc<T>: aRcBox<T> { strong: Cell<usize>, weak: Cell<usize>, value: T }allocated as one block. TheRc<T>is aNonNull<RcBox<T>>. Same forArc<T>but withAtomicUsizeand a separate cache line for the refcount on some impls (studytriomphefor the alternative layout). - Reference cycles:
Rc<RefCell<Node>>with two-way edges leaks. The Drop sequencing matters-when refcount reaches zero, the inner value is dropped before the allocation is freed, so cycles inWeakwork. Pinpreview: introduced here only to flag that interior mutability + self-references is the combination that demandsPin. The full treatment is week 11.
7.3 Lab-"Build a Tracing Rc"¶
Implement TracingRc<T> from scratch using UnsafeCell and NonNull. It must:
- Refcount strong and weak references correctly (study std::rc for the algorithm).
- Log every clone/drop to a thread-local trace buffer.
- Pass Miri (cargo +nightly miri test)-meaning your unsafe code is provably free of undefined behavior under the stacked-borrows model.
7.4 Idiomatic & Clippy Drill¶
clippy::rc_buffer,clippy::redundant_allocation,clippy::arc_with_non_send_sync,clippy::mut_from_ref. The last is a soundness lint-understand why it can never be silenced legitimately.
7.5 Production Hardening Slice¶
- Run your
TracingRclab undercargo +nightly miri test. If it fails, fix the unsoundness; do not silence the diagnostic. Addmirito a nightly CI job ascontinue-on-error: false.
Week 8 - Drop, the Drop Checker, and Destructor Discipline¶
8.1 Conceptual Core¶
Drop::drop(&mut self)runs when a value goes out of scope, whenmem::dropis called, when a panic unwinds, or when aVec/etc. is dropped (each element is dropped in turn).- The Drop checker (
dropck) is the part of the borrow checker that ensures a destructor cannot observe a borrowed value that is itself about to be dropped-the classic "ownedVec<&'a str>where the&'a strreferences something that will outlive'a" hazard. - Drop order:
- Local variables: reverse declaration order within a block.
- Struct fields: declaration order.
- Tuple elements: declaration order.
- Closures: capture order.
8.2 Mechanical Detail¶
#[may_dangle](nightly attribute, used in std internals): a hand-shake by which aDropimpl asserts it will not read its generic-parameter values during drop. This is what allowsVec<&'a T>to drop after&'a T's referent is gone, as long asVec<T>does not access theTs in its destructor.PhantomData<T>and dropck:PhantomData<T>makes the dropck behave as if aTis owned. Necessary for FFI handles where the underlying C type needs lifetime-tracking even though Rust doesn't store one.ManuallyDrop<T>-suppress the destructor entirely. Used forunionfields and for transferring ownership across FFI without a double-free.mem::forget-leak deliberately. Necessary when handing ownership to C code; constitutes a soundness escape hatch but not unsafe (because leaking is safe).std::panic::catch_unwind: how unwinding interacts with destructors. A panic during a destructor's drop = abort (double-panic).
8.3 Lab-"Resource Acquisition Is Initialization"¶
Build a FileLock type wrapping flock(2):
- On construction, acquire an advisory lock.
- On Drop, release it. Even on panic.
- Provide a try_lock constructor returning Result<FileLock, std::io::Error>.
- Add a test that asserts the lock is released after a panic by spawning a child process that panics while holding the lock and observing in the parent that the lock can be re-acquired.
8.4 Idiomatic & Clippy Drill¶
clippy::mem_forget,clippy::drop_non_drop,clippy::let_underscore_must_use,clippy::unnecessary_struct_initialization. Drop discipline is one of the few areas where clippy is mostly about intent signaling.
8.5 Production Hardening Slice¶
- Add a debug-build assertion that
FileLockcannot be moved while held (sketch withPin - full impl deferred to Week 11). Run the full month-2 workspace throughcargo +nightly miri test. Begin aMIRI_NOTES.md` log of every interaction the Miri output forces you to investigate; this becomes a study artifact.
Month 2 Capstone Deliverable¶
A type-system-lab/ workspace containing:
1. lending-iter (week 5)-a no_std - compatible lending iterator crate.
2.bloat-demo(week 6)-the static vs dynamic dispatch comparison, with a written tradeoff analysis.
3.tracing-rc(week 7)-Miri-clean.
4.flock-rs` (week 8)-a working file-lock RAII crate.
Workspace-level CI must add: cargo +nightly miri test, cargo semver-checks. Begin contributing minor doc fixes upstream (rust-lang/rust or a popular crate). Open at least one PR by end of month, however small.
Month 3-Concurrency and Async: Atomics, Lock-Free, Pin, Tokio, Smol¶
Goal: by the end of week 12 you can (a) implement a correct lock-free single-producer single-consumer queue with explicit memory orderings, (b) explain Pin<&mut Self> to a colleague using a self-referential generator example, (c) trace an await from source through state-machine codegen into a Tokio runtime poll, and (d) choose between Tokio, Smol, and embassy based on workload characteristics.
Weeks¶
- Week 9 - Threading, Send, Sync, and the Memory Model
- Week 10 - Channels, Lock-Free Patterns, and
loom - Week 11 - Async Foundations:
Future,Pin,Unpin, the State Machine - Week 12 - Runtimes: Tokio Internals, Smol, Embassy
Week 9 - Threading, Send, Sync, and the Memory Model¶
9.1 Conceptual Core¶
Send= "safe to transfer ownership to another thread."Sync= "safe to share by&Tacross threads" (equivalently:&T: Send).- These are auto traits: structurally derived. A struct is
Sendif all its fields areSend, etc. Opt-out by including aPhantomData<*const ()>or by using!Send/!Syncnegative impls (nightly). - Memory model: Rust's memory model is, today, the C++20 memory model (consume order excluded). You must internalize the happens-before relation, the meaning of
Relaxed,Acquire,Release,AcqRel,SeqCst, and the difference between synchronization and visibility.
9.2 Mechanical Detail¶
std::thread::spawnvsthread::scope: scoped threads (stable since 1.63) allow borrowing from the parent stack frame because the join is guaranteed before the borrow ends. This is the right default for most parallelism.- Atomics (
AtomicUsize,AtomicPtr<T>,AtomicBool, etc.): the API surface is small but every method takes anOrdering. The orderings are: Relaxed-no synchronization; only atomicity. Used for counters where ordering doesn't matter (e.g., metrics).Acquire/Release-pairwise synchronization. AReleasestore synchronizes with anAcquireload that observes the value, establishing happens-before from the store-side's prior writes to the load-side's subsequent reads.AcqRel-both, on RMW (read-modify-write) operations.SeqCst-total order across allSeqCstoperations system-wide. Strongest, slowest, safest if you do not yet know what you're doing.fence: standalone memory barriers. Rarely needed; usually a smell that the abstraction should be different.- Spin loops:
std::hint::spin_loop()emitspause(x86) /yield(ARM). Critical for short-wait spinlocks; missing this hint hurts SMT performance and burns power.
9.3 Lab-"A Correct Spinlock"¶
Implement Spinlock<T> from scratch using AtomicBool:
- lock() spins with Relaxed load, then Acquire CAS.
- unlock() Release stores false.
- Returns a SpinlockGuard<'_, T> whose Drop unlocks.
- Verify with loom (run all interleavings-see week 10) that no two threads enter the critical section.
9.4 Idiomatic & Clippy Drill¶
clippy::mutex_atomic,clippy::mutex_integer,clippy::needless_collect,clippy::should_implement_trait. The first two flag the anti-pattern ofMutex<bool>whereAtomicBoolsuffices.
9.5 Production Hardening Slice¶
- Add a
loomfeature gate to your spinlock crate. Runcargo test --features loomin CI. Fail the build on any model-checker violation.
Week 10 - Channels, Lock-Free Patterns, and loom¶
10.1 Conceptual Core¶
- Channel taxonomy:
- MPSC (multi-producer, single-consumer):
std::sync::mpsc,crossbeam_channel,tokio::sync::mpsc. - SPSC (single-producer, single-consumer):
rtrb,ringbuf. Lock-free, often wait-free. - MPMC:
crossbeam_channel,flume. Generally lock-free with backoff. - Broadcast:
tokio::sync::broadcast. Many readers, lossy if slow. - Watch:
tokio::sync::watch. Single-value, last-write-wins. - Lock-free vs wait-free:
- Lock-free: at least one thread makes progress at any time.
- Wait-free: every thread completes in bounded steps. Wait-free is much harder; most real-world "lock-free" code is lock-free, not wait-free.
- The ABA problem: a CAS that compares a pointer can succeed even though the pointer was freed and re-allocated to the same address. Solutions: hazard pointers, epoch-based reclamation (
crossbeam-epoch), or tagged pointers.
10.2 Mechanical Detail¶
crossbeam-epochfor safe lock-free memory reclamation. Read its source.- Loom: a model checker that exhaustively explores thread interleavings of programs written against its
loom::sync::*shims. Used by Tokio and others to validate concurrent data structures. Read the loom user guide and the tokio test suite. - Cache effects: false sharing. Pad hot atomics to 64 bytes (or 128 on Apple Silicon) with
crossbeam_utils::CachePadded. - Backoff strategies: exponential with jitter, then
thread::yield_now(), thenpark. Readcrossbeam_utils::Backoff.
10.3 Lab-"An SPSC Ring Buffer"¶
Implement a fixed-capacity SPSC ring buffer:
- Two AtomicUsize indices (head, tail), each on its own cache line.
- push and pop use Acquire/Release ordering pairs.
- Validate under loom with at least 4 elements and 3 pushes/pops.
- Benchmark against rtrb with criterion. You should be within 2× on x86_64.
10.4 Idiomatic & Clippy Drill¶
clippy::needless_lifetimes,clippy::missing_const_for_fn. Note that lock-free primitives often cannot beconst fndue to atomic init constraints-explain when.
10.5 Production Hardening Slice¶
- Add
RUSTFLAGS="-Z sanitizer=thread"(nightly) to a CI job. Run your SPSC tests under TSan in addition to Loom. The two catch overlapping but distinct classes of bugs.
Week 11 - Async Foundations: Future, Pin, Unpin, the State Machine¶
11.1 Conceptual Core¶
- A
Futureis a state machine with apollmethod that returnsPoll::Ready(T)orPoll::Pending.awaitis the only legal way to drive a future from inside another future. async fndesugars to a function returning an anonymous type that implementsFuture. The compiler synthesizes the state machine from the control flow of the async block.Pin<P>is a wrapper around a pointerPthat promises the pointee will not be moved (in memory) until it is dropped-unless the pointee isUnpin. This is the linchpin that lets self-referential async state machines work.
11.2 Mechanical Detail¶
- Why
Pin? When an async function awaits, the compiler stores all locals (including references to other locals) in a single struct-the state. References to other fields of the same struct are self-references, valid only while the struct does not move. WithoutPin, aVec::pushthat re-allocates the state would invalidate them.Pinencodes the move-prohibition in the type system. Unpinis an auto trait: a type isUnpinif it is safe to move even when pinned. Most types areUnpin. The exceptions: generators, async blocks, intrusive list nodes, self-referential structs.pin!macro (stable since 1.68): pin a value on the stack ergonomically. Replacestokio::pin!for std users.- The
Waker: an opaque handle the runtime hands to a future so the future can signal "I'm ready to be polled again." Wakers areClone + Send + Syncand have awake()method. - Cancellation in async Rust = dropping the future. Every
awaitpoint is a cancellation point. Code that holds a lock across anawaitand then cancels can deadlock if the lock is not async-aware. This is the single most common source of async bugs in production.
11.3 Lab-"An Async Channel From Scratch"¶
Implement a single-shot async oneshot channel:
- Sender<T> has send(self, T).
- Receiver<T> is Future<Output = Result<T, Cancelled>>.
- Use a single Mutex<State> and a Waker slot.
- Test with both tokio::test and smol::block_on. The result must be runtime-agnostic.
11.4 Idiomatic & Clippy Drill¶
clippy::async_yields_async,clippy::large_futures,clippy::unused_async. The middle lint flags futures whose state struct exceeds a kilobyte-a real performance hazard once you spawn millions.
11.5 Production Hardening Slice¶
- Run your oneshot under
tokio-consoleandconsole-subscriber. Confirm the future is dropped promptly on cancellation. Addtracing::instrumentto every public async fn. Document the size of the generated futures withcargo +nightly rustc -- -Z print-type-sizes.
Week 12 - Runtimes: Tokio Internals, Smol, Embassy¶
12.1 Conceptual Core¶
- A runtime is the executor + reactor + I/O driver that actually polls futures. The
Futuretrait is part ofcore; runtimes are external. - Executor = the part that picks which task to poll next.
- Reactor = the part that registers I/O readiness with the OS (epoll/kqueue/IOCP/io_uring).
- Task = a top-level future plus its scheduling metadata. Tasks are
'static.
12.2 Mechanical Detail-Tokio¶
Read the Tokio source tree in this order:
1. tokio/src/runtime/scheduler/multi_thread/ - the work-stealing scheduler. Each worker has a local LIFO/FIFO hybrid queue plus a global injection queue. Steals from peer queues on starvation.
2.tokio/src/runtime/io/driver.rs - wraps mio (which wraps epoll/kqueue/IOCP). I/O readiness wakes the relevant task's Waker.
3. tokio/src/runtime/time/ - hashed wheel timer fortokio::time::sleep.
4.tokio/src/sync/notify.rsandmutex.rs - async-aware synchronization. Note the intrusive linked list of waiters.
12.3 Mechanical Detail-Smol & async-std¶
- Smol's executor (
async-executor) is a simpler, single-file reactor. Read it; you can understand the entire stack in an afternoon. async-stdis now in maintenance; mention only for historical context.embassyfor embedded: an executor that runs on bare-metal Cortex-M without an OS. Uses interrupt-driven wakers.
12.4 Lab-"Roll-Your-Own Mini Executor"¶
Build a single-threaded executor in ~150 lines:
- A VecDeque<Arc<Task>> ready queue.
- Task holds a Mutex<Pin<Box<dyn Future>>> and implements ArcWake (or Wake on stable).
- block_on polls the root future; auxiliary spawn adds tasks.
- Run a small TCP echo server on top using polling (the same crate Smol uses) for I/O.
12.5 Idiomatic & Clippy Drill¶
clippy::redundant_async_block,clippy::manual_async_fn,clippy::should_panic_without_expect. Read the Tokio style guide and adopt its task-naming conventions.
12.6 Production Hardening Slice¶
- For your mini-executor, add a
tracingsubscriber and a panic hook that aborts the process (so a panicked task does not silently disappear). This is the same pattern Tokio'sunhandled_panic = "shutdown_runtime"enables-set it on every Tokio runtime you create.
Month 3 Capstone Deliverable¶
A concurrency-lab/ workspace:
1. spinlock-rs (week 9)-Loom-verified.
2. spsc-ring (week 10)-Loom + TSan + Criterion benches.
3. oneshot-rs (week 11)-runtime-agnostic, Miri-clean.
4. mini-exec (week 12)-under 200 LoC, runs the TCP echo demo.
CI gates: Loom, Miri, TSan (nightly), Criterion regression tracking. Add a one-page architectural ADR (Architectural Decision Record) for each crate explaining the ordering choices. ADRs become source-of-truth artifacts in subsequent months.
Month 4-Unsafe Rust, FFI, and Macros¶
Goal: by the end of week 16 you can (a) write an unsafe fn whose safety contract is documented well enough to survive a security review, (b) bind to a non-trivial C library and also expose a Rust library to C consumers, (c) write a macro_rules! that respects hygiene and can be re-imported, and (d) ship a procedural macro that derives a non-trivial trait with custom attributes and useful diagnostics.
Weeks¶
- Week 13 - Unsafe Rust: Raw Pointers, NonNull, MaybeUninit, UB
- Week 14 - FFI: Calling C, Being Called By C
- Week 15 - Declarative Macros (
macro_rules!) - Week 16 - Procedural Macros
Week 13 - Unsafe Rust: Raw Pointers, NonNull, MaybeUninit, UB¶
13.1 Conceptual Core¶
unsafeis not "turn off the borrow checker." It unlocks five extra capabilities:- Dereference raw pointers.
- Call
unsafe fns. - Implement
unsafe traits. - Access
static mut(now discouraged in favor ofSyncUnsafeCell). - Access fields of
unions. - The borrow checker, lifetime checker, type checker-all still run.
unsafewidens ability, it does not weaken checks. - The discipline is safety contracts: every
unsafe fnand everyunsafe { ... }block must be paired with a comment articulating the invariants the caller is asserting. The community standard is// SAFETY: ...comments, scanned byclippy::undocumented_unsafe_blocks.
13.2 Mechanical Detail¶
*const Tvs*mut T-the variance differs (*mut Tis invariant inT), the legality of forming references differs (you may not produce&Tfrom a*const Taliasing a&mut T), but otherwise they behave the same. The mut/const distinction is documentation, not enforcement.NonNull<T>-a wrapper around*mut Twith the niche optimization (the null bit pattern is forbidden). Use it for FFI handles and as the storage primitive inBox/Rc/Arc.MaybeUninit<T>-the way to manipulate uninitialized memory legally.mem::uninitializedis deprecated for soundness reasons;MaybeUninitis the replacement. InternalizeMaybeUninit::write,assume_init,assume_init_ref.- Provenance: a pointer carries not just an address but a provenance tag indicating which allocation it derives from.
ptr::with_addrandptr::map_addrare the safe ways to manipulate addresses without losing provenance. Read the strict-provenance proposal (std::ptrmodule docs). - The Rustonomicon's UB list: dangling references, null references, misaligned references, mutable aliasing, type confusion (transmuting padding), data races. Memorize.
13.3 Lab-"A Sound Vec"¶
Re-implement Vec<T> from scratch (the Nomicon's chapter 9 walk-through is the reference). Requirements:
- RawVec allocator wrapper handling growth.
- ZST (zero-sized type) handling-Vec<()> must work without ever allocating.
- Drop correct under panic in T::drop.
- Iteration via IntoIter with proper drop on partial consumption.
- Pass Miri on every public method.
13.4 Idiomatic & Clippy Drill¶
clippy::undocumented_unsafe_blocks,clippy::multiple_unsafe_ops_per_block,clippy::transmute_ptr_to_ref,clippy::cast_ptr_alignment. Each maps to a documented UB class.
13.5 Production Hardening Slice¶
- Run your
Veclab undercargo +nightly miri test -Zmiri-strict-provenance. Document each Miri diagnostic and the fix in aSAFETY_LOG.md. This document is the deliverable, not the code.
Week 14 - FFI: Calling C, Being Called By C¶
14.1 Conceptual Core¶
- The C ABI is the lingua franca. Rust can both consume it (
extern "C" fndeclarations) and expose it (#[no_mangle] pub extern "C" fn). - Two directions of pain:
- Calling C from Rust-bindings, header parsing (bindgen), null-pointer discipline,
CStr/CStringlifetimes, errno. - Calling Rust from C-symbol mangling, panic discipline (panics across FFI = UB), opaque pointers, version-stable headers (cbindgen).
14.2 Mechanical Detail¶
bindgen: parses C headers via libclang, emits Rustexterndeclarations. Configure viawrapper.handbuild.rs. Common pitfalls: bitfield representation, function-like macros (not handled), inline functions (must be static-libbed separately).cbindgen: the inverse. Reads your Rust source and emits a C header. Requires acbindgen.toml. Generated headers should be checked intoinclude/and CI-verified.#[repr(C)]is mandatory for any type crossing the boundary.#[repr(transparent)]for newtype wrappers around FFI primitives.- Strings:
CStris unsized borrowed,CStringis owned. Never assume a*const c_charis UTF-8-convert throughCStr::from_ptrand thento_str(). - Panic safety across FFI: panics that unwind across an
extern "C"boundary are UB. Wrap everyextern "C" fnbody instd::panic::catch_unwindand convert panics to error codes, or compile withpanic = "abort". - errno: thread-local in glibc; access via
std::io::Error::last_os_error()immediately after a syscall.
14.3 Lab-"Bind a Real C Library and Expose a Rust One"¶
Two parts:
1. Consume: write Rust bindings to libsodium's crypto_secretbox family. Use bindgen for the raw layer, then wrap in safe Rust (own the keys with Zeroizing<[u8; 32]>, use typed nonces, return Results).
2. Expose: take your parse-units crate from Month 1 and ship a C-callable parse_units_c library with a cbindgen - generated header. Provide aMakefile` that links a tiny C program against it.
14.4 Idiomatic & Clippy Drill¶
clippy::not_unsafe_ptr_arg_deref,clippy::missing_safety_doc,clippy::fn_to_numeric_cast_any,clippy::transmute_ptr_to_ptr. The first two are FFI-specific safety doc enforcers.
14.5 Production Hardening Slice¶
- Cross-compile your
parse-unitsC library toaarch64-unknown-linux-gnuandx86_64-unknown-linux-musl. Verify the resulting.so/.aartifacts withnmandreadelf. Add this to CI as a matrix build.
Week 15 - Declarative Macros (macro_rules!)¶
15.1 Conceptual Core¶
macro_rules!is a pattern-matching macro system operating on token trees. It is hygienic by default-identifiers introduced in the macro do not collide with identifiers in the call site (with subtle exceptions you must learn).- Designators:
expr,ident,ty,pat,tt,block,stmt,path,meta,vis,lifetime,literal,item. Each constrains what tokens the matcher accepts and how they may be re-emitted. - Repetition:
$( ... )*,$( ... )+,$( ... ),*. The macro author is responsible for handling empty/non-empty cases.
15.2 Mechanical Detail¶
- Hygiene exceptions:
$crateis the canonical way to refer to the defining crate. Without it,vec![1,2,3]would break if the user shadowedstd::vec. Use$crate::in every path emitted by a macro. - Recursion: macros may invoke themselves. The recursion limit is
#![recursion_limit = "256"]by default-raise it explicitly if needed (and document why). - TT munching: a pattern where the macro consumes tokens one at a time recursively. The standard idiom for parsing custom DSLs in
macro_rules!. - Re-export discipline:
#[macro_export]makes a macro visible at the crate root. The 2018+ edition allowspub useof macros, which is the modern preferred path.
15.3 Lab-"A hashmap! Macro With Diagnostics"¶
Implement a hashmap! macro:
- hashmap! { "a" => 1, "b" => 2 } produces a HashMap.
- Trailing comma allowed.
- Type-checks: a typo like hashmap! { "a" => 1, "b" -> 2 } should produce a useful error pointing at the bad token (use compile_error! strategically).
- Pre-allocates with HashMap::with_capacity.
15.4 Idiomatic & Clippy Drill¶
clippy::crate_in_macro_def(use$crate::),clippy::single_call_fn,clippy::useless_format. Read the Little Book of Rust Macros sections on hygiene and TT munching.
15.5 Production Hardening Slice¶
- Add
cargo-expandto your dev tooling. Inspect the expansion of every macro you ship; commit a sample expansion as a doctest. This is your guard against silent semantic drift in macro maintenance.
Week 16 - Procedural Macros¶
16.1 Conceptual Core¶
- Procedural macros are compiled crates of type
proc-macrothat takeTokenStreamin and returnTokenStreamout. Three flavors: #[derive(MyTrait)]-extends a struct/enum with trait impls.- Attribute macros (
#[my_attr])-wholesale rewrite of an item. - Function-like (
my_macro!(...))-likemacro_rules!but with arbitrary computation. - The toolchain:
proc-macro2(a re-export wrapper that allows non-proc-macro testing),syn(parser),quote(token-stream building viaquote! {}).
16.2 Mechanical Detail¶
- Spans: every token carries a
Spanrecording its source location. To produce error messages that point at user code (not macro code), preserve spans across transformations.syn::Error::new_spanned(&item, "msg").to_compile_error()is the idiom. - Hygiene in proc macros: weaker than
macro_rules!. By default, identifiers are call-site hygiene; you must explicitly useSpan::mixed_site()for hygiene-sensitive identifiers. - Diagnostics: stable proc macros emit errors via
compile_error!token streams. Nightly hasproc_macro::Diagnosticwith structured suggestions (used byserdeon nightly). - Testing:
trybuildfor compile-fail tests;cargo expandfor snapshot tests (instaworks well here).
16.3 Lab-"All Three Flavors"¶
Build the dtolnay/proc-macro-workshop exercises end-to-end:
1. derive_builder - derive a builder pattern with field-level attributes for renaming and each-element setters.
2.seq - function-like macro seq!(N in 0..8 { ... }) that emits N expansions.
3. `sorted - attribute macro that enforces enum-variant or match-arm sortedness with proper spans on errors.
This workshop is the gold standard for proc-macro pedagogy. Do all of it.
16.4 Idiomatic & Clippy Drill¶
- Proc-macro crates have their own pitfalls: avoid panicking in your macro (always emit
compile_error!), avoid pulling in heavy deps (every dep slows downstream builds), and feature-gateproc-macro2'snightlyfeature for span fidelity.
16.5 Production Hardening Slice¶
- Add
trybuildUI tests. Pin the toolchain (UI test snapshots are toolchain-sensitive). Addcargo expandsnapshot tests viainsta. Both are in CI; both must pass on nightly and the pinned stable.
Month 4 Capstone Deliverable¶
A unsafe-ffi-macros/ workspace:
1. mini-vec (week 13)-Miri-clean re-implementation of Vec.
2. safe-sodium (week 14)-bindgen + cbindgen, cross-compiled.
3. hashmap-macro (week 15)-published with a doctest of cargo expand output.
4. derive-workshop (week 16)-all three proc-macro-workshop exercises.
CI gates additions: cargo expand - based snapshot tests,trybuildUI tests, cross-compilation matrix (x86_64-unknown-linux-musl, aarch64-unknown-linux-gnu, x86_64-pc-windows-gnu). Open at least one PR against a real crate fixing anunsafeblock's missing SAFETY comment (clippy::undocumented_unsafe_blocks` will surface candidates in the ecosystem).
Month 5-Production Architecture: Hexagonal, Zero-Copy I/O, Observability, Testing¶
Goal: by the end of week 20 you can (a) lay out a non-trivial service following hexagonal/ports-and-adapters and justify each boundary, (b) move bytes from a socket through a parser into application state without copying, (c) instrument a service end-to-end with tracing and OpenTelemetry, and (d) ship a test suite combining unit, integration, property-based, and fuzz harnesses.
Weeks¶
- Week 17 - Hexagonal Architecture and Domain Modeling in Rust
- Week 18 - Zero-Copy I/O and the Poll-Based Model
- Week 19 - Observability: tracing, metrics, OpenTelemetry
- Week 20 - Testing Strategy: Unit, Property, Fuzz, Miri, Integration
Week 17 - Hexagonal Architecture and Domain Modeling in Rust¶
17.1 Conceptual Core¶
- Hexagonal (ports-and-adapters) treats the domain as the center, with explicit ports (traits) describing the interactions the domain needs from the outside world, and adapters (concrete impls) plugging in real I/O. The compiler enforces the boundary; the trait is the contract.
- Rust is unusually good for hexagonal architecture: trait objects (or generics) at the seam,
Resultfor error propagation,cargoworkspaces for crate-level enforcement of dependency direction. - Type-driven design: make illegal states unrepresentable. A
Userwhose email might be unverified is a different type from aVerifiedUser. This is the New Type pattern operating at scale.
17.2 Mechanical Detail¶
- Workspace layout for a hexagonal service:
Each adapter crate depends on
service/ crates/ domain/ # pure types and traits, no I/O deps application/ # use cases, depend on domain adapters/ postgres/ # impl domain ports against sqlx kafka/ # impl event-bus port http/ # impl HTTP-handler port using axum bin/ api/ # wires adapters into a runnable binarydomainand nothing else from this workspace. Enforce withcargo-deny[bans]rules andcargo modulesgraph checks. - Ports as traits: prefer
async_traitonly when needed (allocation per call). When the trait is single-impl per binary, use a generic parameterService<Repo: UserRepo>to avoid theBox<dyn>cost. - Error boundaries: domain errors are typed enums; adapter errors map to domain errors at the seam. Never let a
sqlx::Errorleak to the HTTP handler-it's a layering violation and a security risk (it leaks schema details).
17.3 Lab-"A Hexagonal URL Shortener"¶
Build a workspace implementing a URL shortener:
- domain crate: ShortUrl, UrlAlias newtypes, a UrlRepository trait.
- application crate: Shorten, Resolve use cases.
- adapters/postgres crate: implements UrlRepository with sqlx.
- adapters/http crate: axum handlers using the application layer.
- bin/api crate: composition root.
- An adapters/in-memory crate used by tests, so application logic is testable without a database.
17.4 Idiomatic & Clippy Drill¶
clippy::module_name_repetitions,clippy::too_many_lines,clippy::struct_field_names. These shape API ergonomics, not correctness-but in a hexagonal codebase, ergonomics is maintainability.
17.5 Production Hardening Slice¶
- Add
cargo deny check bansrules forbiddingdomainfrom depending on any I/O crate (tokio,sqlx,reqwest, etc.). The CI must fail on a violation. This is the architectural test.
Week 18 - Zero-Copy I/O and the Poll-Based Model¶
18.1 Conceptual Core¶
- Zero-copy in the small means avoiding
memcpybetween buffers. In the large it means retaining the same allocation from kernel boundary through parsing into application data structures. - The poll-based model (epoll/kqueue/IOCP) returns readiness, not data. The application reads when ready, into a buffer it owns. This is the model
mioexposes;tokiobuilds on top of it. io_uringis the alternative completion-based model on modern Linux: the application submits a request, the kernel performs it and signals completion. Better throughput at high QPS; integrating it cleanly with Rust's borrow model is non-trivial (tokio-uring,glommio,monoio).
18.2 Mechanical Detail¶
bytes::BytesandBytesMut: refcounted byte buffers that support cheap slicing and splitting.Bytes::sliceproduces a newBytesthat points into the same allocation. The cornerstone of zero-copy parsing pipelines.AsyncRead/AsyncWrite(Tokio): the async analogue ofRead/Write. The tokio variants take aReadBufto allow zero-init buffers; the futures-rs variants (used by Smol) use&mut [u8].- Vectored I/O:
readv/writev.IoSliceandIoSliceMutin std. Avoids small-write coalescing copies. sendfile(2)andsplice(2): kernel-mediated copy avoidance for proxy workloads. Wrappers exist innixandrustix.- Parsers that borrow from input:
nomandwinnowproduce&'a [u8]references into the source buffer.serdewith#[serde(borrow)]on&'a strfields. Combine withBytesto keep allocations alive.
18.3 Lab-"A Zero-Copy Line Protocol"¶
Build a server speaking a minimal newline-delimited protocol:
- Read into a BytesMut with try_read_buf.
- Parse line-by-line with winnow, yielding &[u8] slices.
- Push each parsed message into a downstream channel as a Bytes (cloned cheaply, shared with the parser's allocation).
- Benchmark with wrk or tcpkali. Inspect with perf and confirm __memcpy is not a hot frame.
18.4 Idiomatic & Clippy Drill¶
clippy::read_zero_byte_vec,clippy::unbuffered_bytes,clippy::needless_collect. Each is a hint that you're allocating where a `Bytes - style flow would suffice.
18.5 Production Hardening Slice¶
- Add
tokio-consoleinstrumentation. Settokio_unstableinRUSTFLAGS(only in dev/profile builds). Run a 60-second load test and capture aperf recordflamegraph. Commit the SVG.
Week 19 - Observability: tracing, metrics, OpenTelemetry¶
19.1 Conceptual Core¶
- Three pillars: logs, metrics, traces. In Rust, the de-facto crates are
tracing(logs+spans),metrics(counters/gauges/histograms), andopentelemetry(export to OTLP collectors). - Spans are the structured-logging analog of stack frames: a span represents a unit of work, may contain child spans, and carries fields.
tracinginstruments your code with macros. Subscriberis the consumer of spans/events: pretty-printer, JSON logger, Jaeger/OTLP exporter, or a custom sink. Subscribers compose viaLayers.
19.2 Mechanical Detail¶
- Replace
log - crate users withtracing(thelog - compat shim is adequate). Place#[tracing::instrument]on every async use case in the application layer. Never on tight loops in hot paths-it has overhead. tracing-subscribersetup:EnvFilterfor runtime log-level control;fmt::Layerfor stdout;opentelemetry::tracinglayer for distributed tracing.- Metrics:
metricsfacade +metrics-exporter-prometheusfor/metricsscraping. Counters for events, gauges for resource levels, histograms for latencies. UseHistogramnot `Summary - Prometheus aggregation matters. - Cardinality discipline: a label like
user_idhas unbounded cardinality and will OOM Prometheus. Tag withtenant_id,endpoint,status_classonly.
19.3 Lab-"Add Observability to the Hexagonal URL Shortener"¶
Take week 17's URL shortener and add:
- tracing::instrument on every use case, with explicit fields (no PII).
- Prometheus /metrics endpoint with request counts and per-endpoint latency histograms.
- OTLP export to a local Jaeger via docker-compose.
- A flamegraph.svg from a 30-second load test, committed.
19.4 Idiomatic & Clippy Drill¶
clippy::print_stdout,clippy::print_stderr,clippy::dbg_macro. Production code logs throughtracing; these lints catch slip-ups.
19.5 Production Hardening Slice¶
- Configure log/metric redaction at the subscriber layer. Demo: a request body containing an email address must not appear in logs. Add a regex-based redaction layer; unit-test it. This is a compliance prerequisite.
Week 20 - Testing Strategy: Unit, Property, Fuzz, Miri, Integration¶
20.1 Conceptual Core¶
- A production Rust codebase has five test surfaces:
- Unit (
#[test]inmod tests)-fast, in-crate, mock-free where possible. - Integration (
tests/directory)-public-API only, one binary per file. - Property-based (
proptest,quickcheck)-invariants, not examples. - Fuzzing (
cargo-fuzzwith libFuzzer, orafl.rs)-adversarial inputs. - Model checking / Miri-concurrency interleavings and UB detection.
- Each surface answers a different question. Skipping one leaves a class of bugs uncovered.
20.2 Mechanical Detail¶
proptest: shrinking-aware property testing.proptest! { #[test] fn round_trip(x in 0..1000u32) { ... } }. Use it for serialization round-trips, parsers, sort/search.cargo-fuzz:cargo +nightly fuzz init && cargo +nightly fuzz add my_target. Each target is afuzz_target!(|data: &[u8]| { ... }). Run continuously on a CI runner, store the corpus.arbitrarycrate: deriveArbitraryfor your domain types so fuzz inputs are well-typed.instafor snapshot tests: golden-file testing for proc-macro expansions,Debugoutputs, serialized formats.testcontainers: spin up Postgres/Kafka/Redis from inside#[tokio::test]. The right tool for adapter integration tests in a hexagonal codebase.
20.3 Lab-"Test-Pyramid the URL Shortener"¶
- Property-test the alias-generation function (idempotent, collision-resistant under birthday-bound assumptions).
- Fuzz the public HTTP handlers via the
axum::Routerdirectly (no socket). - Integration-test the Postgres adapter against a real Postgres in
testcontainers. - Snapshot-test the OpenAPI spec with
insta. - Achieve 90%+ coverage per
cargo-llvm-cov.
20.4 Idiomatic & Clippy Drill¶
clippy::should_panic_without_expect,clippy::panic_in_result_fn,clippy::tests_outside_test_module.
20.5 Production Hardening Slice¶
- Add a continuous-fuzzing job (e.g., on a scheduled GitHub Action). Persist the corpus as an artifact. Any new crash file is a P0 issue; document the triage flow in
SECURITY.md.
Month 5 Capstone Deliverable¶
A production-shaped url-shortener-prod/ workspace:
- Hexagonal layout with adapter isolation enforced by cargo deny.
- Zero-copy parser on the wire.
- tracing + Prometheus + Jaeger.
- Five test surfaces wired into CI.
- A one-page RUNBOOK.md describing alarms, dashboards, and rollback procedures.
This is the first artifact in the curriculum that resembles a real production service. Treat it as your portfolio piece.
Month 6-Mastery: Custom Data Structures, no_std, Compiler Internals, Capstone¶
Goal: by the end of week 24 you have shipped one capstone deliverable in your chosen track (compiler / fintech / kernel) and can defend every design decision in a senior-level technical interview.
Weeks¶
- Week 21 - Implementing Complex Data Structures From Scratch
- Week 22 -
no_std, Custom Allocators, Embedded Targets - Week 23 - Compiler Internals: MIR, Borrow Check, Codegen
- Week 24 - Capstone Integration, Profiling, Hardening, Defense
Week 21 - Implementing Complex Data Structures From Scratch¶
21.1 Conceptual Core¶
- The std collections (
Vec,HashMap,BTreeMap,VecDeque) are excellent but generic. Real systems regularly need: lock-free hash tables, B-trees specialized to a key shape, intrusive linked lists, slab allocators, MPMC queues with bounded backpressure, log-structured merge trees, skip lists. - Building these from scratch once teaches the patterns: cache-friendly layout, intrusive vs extrusive, the cost of
Box, the role ofNonNullin linked structures.
21.2 Mechanical Detail-three target structures¶
(a) A B-Tree map.
- Branching factor ~6 by default in std; tune for your key/value sizes.
- Inner nodes store keys + child pointers; leaf nodes store keys + values.
- Fixed-capacity arrays ([MaybeUninit<K>; B]) avoid per-key allocations.
- The split/merge invariants are the entire algorithmic content; the Rust difficulty is expressing the invariants in safe code.
(b) A lock-free concurrent hash map.
- Study dashmap (sharded, simpler) and flurry (Java-ConcurrentHashMap port using crossbeam-epoch). Build a sharded version yourself first; only attempt epoch-based after week 22's allocator work.
- Sharding: N shards, each a RwLock<HashMap<K, V>>. Hash key, mod-shard, lock the shard. Trivially correct, scales linearly until shard contention.
- Lock-free: open addressing with atomic CAS on slots, epoch-based reclamation for resizes. This is hard.
(c) An intrusive doubly-linked list.
- The Linux kernel pattern: nodes embed prev/next pointers, list operations are O(1) and zero-allocation.
- In Rust, this requires Pin (nodes can't move once linked) and careful unsafe code. Read intrusive-collections and tokio::sync::notify's waiter list as references.
21.3 Lab-"Pick One and Ship It"¶
Implement one of the three to publishable quality:
- Property-tested against std equivalent.
- Miri-clean.
- Loom-verified (for the lock-free).
- Criterion-benchmarked against std/dashmap/intrusive-collections.
- README explains the algorithmic choice and tradeoffs.
21.4 Idiomatic & Clippy Drill¶
clippy::missing_safety_doc,clippy::undocumented_unsafe_blocks,clippy::pedanticgroup. By now these should fire rarely.
21.5 Production Hardening Slice¶
- Publish to a private registry (or a personal GitHub Packages registry). Tag a
v0.1.0. Runcargo semver-checksagainst the tag from this point on.
Week 22 - no_std, Custom Allocators, Embedded Targets¶
22.1 Conceptual Core¶
#![no_std]removes thestdprelude and the default allocator. Code becomes runnable on bare metal (no OS), in kernels, in WASM-without-runtime, and in embedded MCUs.core(no allocator) andalloc(allocator-required) are the layers belowstd.allocprovidesBox,Vec,String, etc., but requires you to nominate a#[global_allocator].- Custom allocators: implement
core::alloc::GlobalAlloc(for the global one) or the unstableAllocatortrait (for per-collection allocators, on nightly).
22.2 Mechanical Detail¶
- Cross-compilation:
rustup target add thumbv7em-none-eabihf(Cortex-M4F),rustup target add riscv64gc-unknown-linux-gnu,rustup target add wasm32-unknown-unknown.cargo build --target=...and the right linker via.cargo/config.toml. - Embedded toolchain:
cargo-binutils,probe-rsfor flashing,cortex-m,embedded-haltraits,rticorembassyfor concurrency. The embedded ecosystem is its own discipline; the goal here is fluency, not specialization. - Allocators worth knowing:
mimalloc,jemalloc(tikv-jemallocator),snmalloc,bumpalo(arena),linked_list_allocator(no_std heap). Switching the global allocator is one line inmain.rs. - Panic in
no_std: you must provide a#[panic_handler]. Common implementations: spin forever, write to a UART, reset the chip.
22.3 Lab-"Two Targets"¶
- Bare metal: blink an LED on a real or QEMU-emulated Cortex-M target using
embassy. Optional but recommended. - Custom allocator: write a simple bump allocator. Use it as
#[global_allocator]for a smallno_std + allocbenchmark and observe behavior.
22.4 Idiomatic & Clippy Drill¶
clippy::std_instead_of_alloc,clippy::std_instead_of_core,clippy::alloc_instead_of_core. These force the discipline of leveling imports correctly-ano_stdlibrary that imports fromstdwill not compile downstream.
22.5 Production Hardening Slice¶
- Add a CI matrix entry for
cargo build --target=thumbv7em-none-eabihf --no-default-features. Library crates should compile inno_stdmode (gated by a feature flag)-this is a real selling point and a frequent regression source.
Week 23 - Compiler Internals: MIR, Borrow Check, Codegen¶
23.1 Conceptual Core¶
- The Rust compiler is query-based: every piece of derived information (types, traits, MIR, codegen units) is computed lazily and memoized in a query system (
rustc_query_system). - MIR (Mid-level IR) is the IR on which borrow checking, optimization, and constant evaluation run. It is a CFG of basic blocks with statements and terminators, much closer to LLVM IR than HIR.
- Bootstrap:
rustcis written in Rust. To build it from source you compile a stage-0 (downloaded prebuilt) → stage-1 (built by stage-0) → stage-2 (built by stage-1, the deliverable). Understanding bootstrap is half the battle of contributing.
23.2 Mechanical Detail¶
- Clone
rust-lang/rust, run./x.py setup(choosecompilerorlibraryprofile),./x.py build library/std. Plan ~30 minutes for the first build. Subsequent incremental builds are ~minutes. - Read
rustc-dev-guide.rust-lang.orgcover-to-cover. The high-yield chapters: "Overview of the compiler", "Queries", "MIR", "Borrow checking", "Trait resolution". rustc -Z unpretty=mirto dump MIR;rustc -Z unpretty=hirfor HIR. Read the MIR of a small program with a borrow-check error-the diagnostics make sense once you see the IR.- The
E-easyandE-mentorlabels in the rust-lang/rust issue tracker: the on-ramp. Pair withrustc-dev-guide's "your first PR" walkthrough.
23.3 Lab-"Read, Build, Land"¶
- Build rustc from source. Modify a single diagnostic message in
compiler/rustc_borrowck/src/...to add a new help line. Rebuild stage-1 and confirm the new message in - -explain`. - Find an issue with
E-easy. Read the linked discussion. Cross-reference withrustc-dev-guide. Do not yet open a PR; instead, write a one-page plan describing the proposed change. Discuss with a maintainer in the issue comments.
23.4 Idiomatic & Clippy Drill¶
- The rustc codebase has its own
rustc_lintlints. Readcompiler/rustc_lint_defs/src/builtin.rsto see how lints are defined; this is the same machinery clippy uses.
23.5 Production Hardening Slice¶
- Configure your shell with
rust-analyzer.checkOnSave.command = "clippy". Adoptcargo +nightly fmt -- --config-path rustfmt.toml. The compiler repo enforces its own rustfmt config; using the same locally avoids surprise CI failures.
Week 24 - Capstone Integration, Profiling, Hardening, Defense¶
24.1 Conceptual Core¶
- The final week is integration, not new material. Bring the chosen capstone (see
CAPSTONE_PROJECTS.md) to merge-ready quality: profile, tune, document, and prepare to defend the design.
24.2 Mechanical Detail-Profiling Toolkit¶
perf+ flamegraphs (flamegraphcrate orcargo flamegraph): the daily driver for CPU time analysis on Linux.samply: aperf recordalternative producing Firefox-Profiler-compatible output. Lower friction.heaptrackfor allocator profiling.dhat-rsfor in-process heap snapshots in tests.cargo-llvm-lines: which generic instantiations are blowing up codegen?cargo-bloat: which symbols dominate the binary?tokio-console: which tasks/locks are starving?pprof-rs+criterion: capture flamegraphs directly from benchmarks.
24.3 Mechanical Detail-Hardening Pass¶
By now, every previous module has fed the hardening/ workspace. Roll it up into one final release-checklist.md:
- [ ] lto = "fat", codegen-units = 1, panic = "abort", strip = "symbols" in release.
- [ ] PGO instrumented build → representative workload → optimized build (see Appendix A).
- [ ] BOLT post-link optimization on the final binary if available.
- [ ] Cross-compilation matrix green for all target triples.
- [ ] cargo deny, cargo audit, cargo geiger, cargo semver-checks all green.
- [ ] Miri clean on unsafe code paths.
- [ ] Loom clean on concurrent paths.
- [ ] Property tests, fuzz harness, snapshot tests in CI.
- [ ] tracing + metrics + traces wired and tested.
- [ ] Runbook, ADRs, threat model, and SECURITY.md present.
- [ ] Reproducible build verified via cargo vet or a SOURCE_DATE_EPOCH-pinned Docker image.
24.4 Lab-"Defend the Design"¶
Schedule a 45-minute mock review with a senior peer (or record yourself if none is available). Present:
- The architecture diagram.
- One slide per non-obvious decision (e.g., "why sharded RwLock instead of dashmap", "why tokio over glommio").
- A live demo of the test suite.
- A live demo of one production-hardening tool (PGO, BOLT, or fuzz corpus).
The deliverable is the defense, not the slides. If you cannot answer "what fails first under load?" or "what is your worst-case allocation pattern?", you have not yet finished the curriculum.
24.5 Idiomatic & Clippy Drill¶
- Final pass:
cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::cargo. Fix or#[allow]with a rationale comment for each remaining lint. Zero unjustified allows.
24.6 Production Hardening Slice¶
- Tag the capstone repo
v1.0.0. Generate a release artifact withcargo dist. Sign the release withcosignor a Sigstore-compatible flow. The final commit hash is the artifact you reference on your resume.
Month 6 Deliverable¶
The chosen capstone (see CAPSTONE_PROJECTS.md):
- Compiler track: a merged or in-review PR against rust-lang/rust.
- Fintech track: a benchmarked matching engine in a public repo.
- Kernel track: a rust-for-linux driver with KUnit tests.
Plus the hardening/ workspace, now a publishable Cargo template under your name.
You are done. The next steps are no longer pedagogical; they are professional.
Appendix A-Production Hardening Reference¶
This appendix consolidates the hardening slices distributed throughout the curriculum. By week 24 the reader's hardening/ workspace should contain working examples of every section below.
A.1 Compiler-Driven Optimization¶
A.1.1 Link-Time Optimization (LTO)¶
- What it is: LLVM optimizes across crate boundaries at link time, enabling cross-crate inlining, dead-code elimination on monomorphized paths, and devirtualization.
- Modes:
lto = false(default debug): per-codegen-unit only.lto = "thin": ThinLTO. Parallel, fast, ~95% of fat-LTO's wins. Default for new release builds.lto = "fat": full LTO. Single-threaded, slow, but produces the smallest/fastest binaries. Use for shipped binaries.lto = "off": explicit opt-out (rare).- Combine with
codegen-units = 1for maximum cross-function optimization. The cost is parallelism: a single codegen unit cannot be compiled in parallel. - Cargo profile snippet:
A.1.2 Profile-Guided Optimization (PGO)¶
PGO uses runtime profiles to guide layout, inlining, and branch-prediction hints.
Workflow: 1. Instrument:
2. Run a representative workload with the instrumented binary. The "representative" qualifier is the entire challenge of PGO; a synthetic benchmark gives you a binary tuned for synthetic benchmarks. 3. Merge profiles: 4. Re-build with the profile:RUSTFLAGS="-Cprofile-use=/tmp/pgo/merged.profdata" \
cargo build --release --target=x86_64-unknown-linux-gnu
Expect ~5–15% throughput wins on hot paths. Do not enable PGO on first-shipping binaries; it adds build complexity for a marginal win. Enable once the workload is well-characterized.
A.1.3 BOLT (Binary Optimization and Layout Tool)¶
- BOLT is a post-link optimizer that rewrites the binary based on
perf recorddata. Stacks on top of PGO. - Workflow: build with - Wl,-q
to keep relocations, runperf recordon a representative workload, runllvm-bolt` with the perf data. - Used in production by Meta and the rustc team itself (rustc's own binary is BOLTed).
A.1.4 target-cpu=native and feature gating¶
RUSTFLAGS="-C target-cpu=native"enables every ISA extension the build host supports-not portable. Use only for self-hosted services where the deployment hardware is known.- For portable binaries with runtime feature dispatch:
is_x86_feature_detected!("avx2")plusmultiversionmacro for compile-time fan-out. Pattern fromsimd-json,rav1e.
A.2 Cross-Compilation¶
A.2.1 Targets to know¶
| Triple | Use case |
|---|---|
x86_64-unknown-linux-musl |
Statically linked Linux server binaries; no glibc dependency. |
aarch64-unknown-linux-gnu |
Graviton, Ampere, Apple Silicon Linux VMs. |
aarch64-apple-darwin |
Apple Silicon native. |
x86_64-pc-windows-msvc |
Windows native (preferred over gnu). |
wasm32-unknown-unknown |
Browser/runtime-less WASM. |
wasm32-wasip2 |
Server-side WASM with WASI. |
thumbv7em-none-eabihf |
Cortex-M4F (no_std, embedded). |
riscv64gc-unknown-linux-gnu |
RISC-V Linux. |
A.2.2 Toolchain mechanics¶
rustup target add <triple>adds prebuilt std for the target.- For native deps (C libraries), use
cross(cross build --target=...) which uses Docker images with the right toolchains pre-installed. - For pure-Rust crates,
cargo zigbuild(using Zig as the C linker) is a popular zero-config alternative.
A.2.3 Static linking¶
- musl + - C target-feature=+crt-static` produces a fully static binary. Ideal for distroless containers.
- Beware: musl's allocator is slow; for performance-critical static binaries, use
mimallocorjemallocas the global allocator.
A.3 Supply-Chain & Soundness Auditing¶
A.3.1 cargo-deny¶
- License allowlist (
[licenses]). - Banned crates (
[bans])-used in Month 5 to enforce hexagonal layering. - Advisory database integration (
[advisories]) for known CVEs. - Source allowlist (
[sources])-only crates.io and your private registry.
A.3.2 cargo-audit¶
- RustSec advisory database. Run on every CI build.
cargo audit fixfor trivial yanks; for non-trivial CVEs, document the response inSECURITY.md.
A.3.3 cargo-geiger¶
- Counts
unsafeusage in your dependency tree. Outputs a "radioactivity score." - Use as a signal, not a gate:
unsafeper se is not bad. A high score in a leaf crate (e.g., a serializer) is normal; a high score in a domain crate is a smell. - Track the score over time; sudden increases indicate a dependency added unsafe code in a minor bump.
A.3.4 cargo-vet¶
- Mozilla's supply-chain audit tool. Lets your team certify specific crate-version pairs as audited; CI fails if an unaudited dep enters the graph.
- Heavyweight; appropriate for high-assurance teams (browsers, kernels, payment systems).
A.3.5 cargo-semver-checks¶
- Semantic-versioning lint. Catches API breakage that would require a major bump.
- Run on every PR that touches a public crate.
A.3.6 cargo-machete and cargo-udeps¶
- Detect unused dependencies. Each unused dep is a supply-chain liability.
A.4 Soundness Validation¶
A.4.1 Miri¶
- Interpreter for MIR. Detects undefined behavior: dangling pointers, OOB access, type confusion, data races (with - Zmiri-many-seeds`).
cargo +nightly miri test. ~50–100× slower than native. Used in CI as a separate job.- Strict-provenance mode catches a class of bugs the default mode misses: - Zmiri-strict-provenance`.
A.4.2 Loom¶
- Permutation-checker for concurrent code. Replace
std::sync::*andstd::thread::*withloom::sync::*andloom::thread::*under a feature gate;loom::model(|| ...)exhausts interleavings. - Used by
tokio,crossbeam,parking_lot. Use it for any data structure with hand-written atomics.
A.4.3 ThreadSanitizer / AddressSanitizer / MemorySanitizer / LeakSanitizer¶
- Nightly-only via - Z sanitizer=thread|address|memory|leak
. Requires - Z build-std. - Catches dynamic UB at runtime. Stack on top of fuzzing for maximum coverage.
A.4.4 KANI¶
- Bounded model checker for Rust (formal verification). Annotate functions with
#[kani::proof], write harnesses, runcargo kani. - Ideal for cryptographic primitives, parsers, and
unsafeinvariants.
A.5 Reproducibility and Distribution¶
A.5.1 Reproducible builds¶
- Pin
rust-toolchain.toml. - Set
SOURCE_DATE_EPOCHand avoidbuild.rsnon-determinism (no system-time stamps, no environment leaks). - Build inside a deterministic container image (Nix, Bazel, or a pinned Docker tag with content hash).
A.5.2 cargo-dist¶
- Generates GitHub Actions to build cross-platform release artifacts and installers (shell installer, Homebrew formula, MSI).
- The recommended baseline distribution path for a CLI or daemon.
A.5.3 Signing and SBOM¶
cosignfor artifact signatures (Sigstore).cargo cyclonedxorcargo sbomfor CycloneDX/SPDX SBOM generation.- Both are now compliance prerequisites in regulated environments.
A.6 The Hardening Workspace Structure¶
By week 24, the hardening/ workspace should contain:
hardening/
.cargo/config.toml # global RUSTFLAGS, target dirs
rust-toolchain.toml # pinned channel
deny.toml # cargo-deny rules
cargo-vet/ # vet store
ci/
lint.yml # fmt + clippy
test.yml # test + miri + loom + sanitizers
cross.yml # cross-compilation matrix
audit.yml # cargo audit + deny + geiger
fuzz.yml # scheduled continuous fuzzing
pgo.yml # PGO build pipeline
scripts/
pgo.sh # instrument-run-rebuild script
bolt.sh # post-link BOLT pass
bench-baseline.sh # criterion baseline comparison
RELEASE_CHECKLIST.md
SECURITY.md
THREAT_MODEL.md
This is the artifact that should accompany every Rust project you ship after week 24. It is the "boring" half of professional Rust.
Appendix B-Build-From-Scratch Data Structures Reference¶
A working Rust engineer should have implemented each of the following at least once, with property tests, Miri, and (where concurrent) Loom. This appendix sketches the minimal-viable design for each. Full implementations are the labs in Months 3, 4, and 6.
B.1 Single-Threaded Hash Map (Open Addressing, Robin Hood)¶
When: pedagogical only-hashbrown::HashMap is faster than anything you will write.
Design:
- Single Vec<Entry> backing array; entries store (hash, key, value) or a tombstone.
- Robin Hood probing: track each entry's distance from its ideal slot; on insert, swap with any entry whose distance is shorter than the inserter's. This bounds variance in probe length.
- Resize at load factor ~0.875.
- hashbrown (used by std since 1.36) further uses SIMD for parallel probe; that is the next exercise after this one.
Lab outcomes: cache-friendly layout intuition, the cost of generic Hasher, niche optimization in Option<u64> for entry-tag storage.
B.2 B-Tree Map¶
When: ordered iteration, range queries, on-disk indexes (with adapted node sizes).
Design:
- Branching factor B (std uses 6; for cache-line-fit nodes you might use 11–15 depending on key/value sizes).
- Two node kinds: leaf and internal. Implement as enum or as separately-allocated structs with a tag.
- MaybeUninit<K; 2*B-1> for keys; insertions copy-shift, splits move half to a new node.
- Walking the tree mutably is the unsafe-rust master class-see std's BTreeMap source for the canonical handling of NodeRef<BorrowType, K, V, NodeType>.
Lab outcomes: complex unsafe with strong invariants, MaybeUninit for arrays, PhantomData for borrow-type encoding.
B.3 Sharded Concurrent Hash Map¶
When: production reads/writes, when contention is moderate. The pragmatic choice for almost every concurrent map need.
Design:
- N shards, each RwLock<HashMap<K, V>>. N typically 16–64; tune via cargo bench for your contention pattern.
- Hash key, shard_idx = hash % N, lock the shard, perform the op.
- Iteration is delicate: lock shards in order, snapshot or hold the lock for each.
Lab outcomes: the RwLock vs Mutex tradeoff, the cost of Hash + Eq re-evaluation, the dashmap API study.
B.4 Lock-Free Hash Map (Open-Addressed, Epoch-Reclaimed)¶
When: extreme contention, when flurry or dashmap are not enough.
Design:
- Slots are AtomicPtr<Entry> (or tagged atomic word for inline values).
- Insert: probe via CAS, inserting Entry. Failures retry to next slot.
- Delete: tombstone via CAS.
- Resize: allocate new table, transfer entries via help_resize cooperative pattern. Old table reclaimed via crossbeam-epoch once no thread observes it.
- Memory ordering: Acquire/Release on slot CAS; SeqCst only when establishing a total order on resize commits.
Lab outcomes: every paragraph above is its own bug class. This is the hardest data structure in the curriculum and the one that most differentiates expert from journeyman Rust engineers.
B.5 SPSC Lock-Free Ring Buffer¶
When: audio threads, real-time control loops, log producers.
Design:
- Fixed-capacity [UnsafeCell<MaybeUninit<T>>; CAP] (CAP a power of two for cheap modulo).
- head: AtomicUsize, tail: AtomicUsize, each on its own cache line (CachePadded).
- Producer: load tail (Relaxed), check space against head (Acquire), write slot, store tail (Release).
- Consumer: symmetric, swapping head/tail.
- Wait-free per side; needs no CAS, only atomic loads/stores.
Lab outcomes: cache-line awareness, Acquire/Release pairing, the pattern for "check then act" without locks.
B.6 MPMC Bounded Queue (`crossbeam::ArrayQueue - style)¶
When: work-stealing schedulers, bounded work pools.
Design:
- Slot array with AtomicUsize "stamp" per slot encoding (lap, index, occupied flag).
- Producer CAS the stamp from "empty at lap N" to "occupied at lap N".
- Consumer CAS from "occupied at lap N" to "empty at lap N+1".
- The stamp scheme avoids the ABA problem and the need for hazard pointers.
Lab outcomes: encoding state in atomics, lock-free without epoch reclamation, why Vec::reserve is the right initialization shape.
B.7 Intrusive Doubly-Linked List¶
When: kernel code, async runtimes' waker lists, anything that must avoid per-node allocation.
Design:
- The list does not own nodes. Nodes own their Pointers { prev: Option<NonNull<Node>>, next: Option<NonNull<Node>> }.
- Nodes must be Pinned because the list stores raw pointers to them.
- unsafe API: the consumer asserts that nodes outlive the list and are not aliased. This is the pattern in tokio::sync::Notify, parking_lot::WaitQueue, and intrusive-collections.
Lab outcomes: Pin in anger, the safety contract for intrusive structures, why LinkedList in std is rarely the right answer.
B.8 Slab Allocator¶
When: lots of small fixed-size allocations with churn (e.g., per-connection state in a server).
Design:
- A Vec<Slot<T>> where Slot<T> = { value: MaybeUninit<T>, next_free: usize }.
- A free-list head index. Insert: pop free-list, write value, return index. Remove: push index onto free-list, drop value.
- O(1) insert and remove, integer-handle indexing, no per-element allocation.
Lab outcomes: slab crate study, generational indices for ABA defense (see generational-arena), the relationship to ECS storage patterns in game engines.
B.9 Bump Allocator (Arena)¶
When: short-lived per-request allocations, parser ASTs, anything where the lifetime ends together.
Design:
- A Vec<u8> (or chunked list of Vec<u8>s for unbounded growth) and a current offset.
- Allocate: align current, advance, return pointer.
- Drop everything at once (no per-item destructors run by default; see bumpalo::Bump::alloc vs alloc_with).
Lab outcomes: alignment math, pointer arithmetic with provenance, why Rust ASTs and rustc itself use bump allocation.
B.10 Skip List (Concurrent)¶
When: ordered concurrent containers; the foundation of crossbeam-skiplist. Used in RocksDB-style memtables.
Design: - Tower of forward pointers per node, height geometrically distributed. - Concurrent insert: build the new node bottom-up, link levels via CAS. Stale links retry. - Removal: logical (mark deleted) then physical (unlink).
Lab outcomes: lock-free with non-trivial structure, randomization in algorithm design, the alternative to balanced trees in concurrent settings.
B.11 LSM-Tree Memtable + SSTable Pair¶
When: storage engines (RocksDB, LevelDB, Sled-style).
Design: - Memtable: a sorted concurrent map (skip list or sharded BTree) holding recent writes. - WAL (write-ahead log) appended for durability. - On size threshold, flush memtable to an immutable SSTable on disk: sorted key-value pairs with a sparse index and Bloom filter. - Compaction merges SSTables periodically.
Lab outcomes: durability/throughput tradeoffs, memory-mapped vs read-syscall, Bloom filters in practice. Optional capstone-tier.
Difficulty Ranking¶
| Tier | Structures |
|---|---|
| Warmup | Hash map (single-threaded), Slab, Bump |
| Intermediate | B-Tree, Sharded concurrent map, SPSC ring |
| Advanced | MPMC queue, Intrusive list |
| Expert | Lock-free hash map, Skip list, LSM-tree |
Pick at least one from each tier. Ship with property tests, benchmarks, and Miri.
Appendix C-Contributing to rustc: A Playbook¶
Most engineers never contribute to a compiler. The barrier is reputational ("compilers are hard"), not technical. This appendix is the on-ramp.
C.1 Mental Model¶
rustc is a query-driven, incrementally-compiled, multi-stage compiler whose front-end (HIR), middle (MIR), and back-end (LLVM/Cranelift codegen) are loosely coupled by a query system. You do not need to understand all of it to land a useful PR. You need to understand:
- The query system at a high level (queries memoize derived data; recompiles invalidate dependent queries).
- The MIR if you are touching borrow check, optimizations, or const-eval.
- The HIR and resolver if you are touching name resolution or module structure.
- The lint infrastructure if you are adding a lint (the easiest first PR).
- The diagnostic infrastructure if you are improving an error message (the easiest possible first PR).
C.2 The Pipeline, in 30 Seconds¶
Source
│ Lexer ── tokens
│ Parser ── AST
│ Macro expansion + name resolution ── expanded AST
│ AST → HIR lowering
│ Type checking + trait solving (on HIR)
│ HIR → THIR → MIR
│ Borrow checking (on MIR)
│ MIR optimization (inlining, const-prop, dead-code, dataflow analyses)
│ MIR → LLVM IR (or Cranelift IR)
│ LLVM optimization + codegen
▼
Object code → linker → binary
Each arrow above is a query. Each query has inputs and outputs. The tcx: TyCtxt<'tcx> is the god-object that holds the query context.
C.3 Setting Up¶
git clone https://github.com/rust-lang/rust
cd rust
./x.py setup # choose 'compiler' or 'library' profile
./x.py check # ~5 min on a fast machine; sanity check
./x.py build library/std --stage 1
Edit, then:
For most PRs, stage 1 is enough. Only stage 2 fully self-bootstraps; reserve it for final verification and ./x.py test.
Use rust-analyzer with the rustc workspace; the maintainers ship a config (.vscode/settings.json template in the repo root).
C.4 Where the Easy Wins Are¶
In rough order of difficulty, the on-ramp issues:
C.4.1 Diagnostic improvements¶
- An error like
error[E0308]: mismatched typeswhosenote:line could be more helpful. Search the issue tracker forA-diagnostics+E-easy. - Touch
compiler/rustc_*/messages.ftl(Fluent translations) and the corresponding*.rssite of emission. Often <30 lines.
C.4.2 New clippy lints¶
- The clippy repo (
rust-lang/rust-clippy) maintains agood-first-issuequeue. Each lint is roughly: declare inclippy_lints/src/lib.rs, write anEarlyLintPassorLateLintPassimpl, add UI tests, document. - Lower bar than rustc proper for review.
C.4.3 Small library PRs (library/std, library/core, library/alloc)¶
- API additions need an ACP (API Change Proposal) before code; small soundness fixes do not.
- Library team ships fast and reviews kindly; great track for a first PR.
C.4.4 Diagnostic suggestion / rustfix integration¶
- Existing errors that lack - -help
suggestions. Adding a structured suggestion thatcargo fix` can apply is high-impact and well-bounded.
C.4.5 rustdoc improvements¶
- Search ranking, intra-doc-link resolution, theme tweaks. Self-contained crate (
src/librustdoc/).
C.4.6 MIR optimization passes¶
compiler/rustc_mir_transform/. Each pass is a struct implementingMirPass. Adding a new pass is medium-difficulty; fixing an existing pass is easier and more common.- This is where the curriculum lands you by week 23. Examples of tractable PRs: tightening an existing const-prop, adding a new dataflow analysis for a specific shape.
C.4.7 Don't start here (yet)¶
- Trait solver (chalkification is in flight; touching it requires deep context).
- Borrow checker proper (Polonius is similarly in flight).
- Codegen / LLVM interface.
- Bootstrap.
C.5 The First-PR Workflow¶
- Pick an issue. Comment
@rustbot claimto assign yourself. - Write a one-paragraph plan in the issue or in a draft PR description: what you'll change, where, and how you'll test.
- Ask early. The
t-compiler/helpZulip stream and the issue's mentor (named in theE-mentorlabel) want to be asked. Pre-PR clarification beats a rejected PR every time. - Implement, test, format:
./x.py test compiler/rustc_<crate> --stage 1./x.py test src/test/ui --stage 1if you've changed UI tests (most diagnostics PRs)../x.py fmt- Open the PR referencing the issue. Add the
r?reviewer suggested by the issue orr?@rustbotfor triage. - Address review-expect 1–3 rounds. The reviewers are unpaid volunteers; be patient and concrete.
@bors r=<reviewer>approves; bors will batch and merge. Your name is in the changelog.
C.6 The Compiler Reading Map¶
When the dev guide is dense, these source files are the most yield-per-page reads:
| File | What it teaches |
|---|---|
compiler/rustc_middle/src/ty/context.rs |
The TyCtxt: how everything is queried. |
compiler/rustc_middle/src/mir/mod.rs |
MIR: Body, BasicBlock, Statement, Terminator. |
compiler/rustc_borrowck/src/lib.rs |
The borrow checker entry point. |
compiler/rustc_mir_transform/src/lib.rs |
MIR passes: how they're declared and ordered. |
compiler/rustc_lint_defs/src/builtin.rs |
How lints are declared. |
compiler/rustc_resolve/src/lib.rs |
Name resolution. |
compiler/rustc_hir/src/hir.rs |
The HIR data model. |
compiler/rustc_codegen_ssa/src/base.rs |
The codegen façade above LLVM. |
Read in this order: MIR → borrow check → MIR transform → lints → resolver → HIR → codegen.
C.7 Adjacent Targets if rustc Is Too Heavy¶
rust-clippy-same machinery, smaller surface, faster review.rust-analyzer-entirely different codebase but high-impact contributions.cargo-large but well-modularized; build-system contributions.rustup-small, contained.miri-interpretive twin of rustc; PRs here teach you the abstract machine.stdlib-library/std,library/core. Often the easiest first merge.
A merged PR in any of these is a credible signal in interviews and grant applications.
C.8 Calibration¶
A reasonable goal for a curriculum graduate:
- By end of week 23: a PR open against
rust-clippy(a new lint or a false-positive fix) orrust-lang/rust(a diagnostic improvement). - By end of capstone: that PR merged.
- 6 months post-curriculum: a non-trivial PR-a new MIR pass, a stabilized API, a soundness fix.
These are realistic timelines. The maintainers prioritize correctness over speed; do not be discouraged by a 4-week review cycle.
Capstone Projects-Three Tracks, One Choice¶
The Month 6 capstone is the deliverable that converts this curriculum from study into evidence. Pick one track. The work performed here is the work you describe in interviews and link from a portfolio.
Track 1-Compiler / Tooling¶
Outcome: a merged PR (or one in advanced review) against rust-lang/rust, rust-clippy, rust-analyzer, or cargo.
Suggested scopes (ranked by tractability)¶
- Diagnostic improvement in
rustc. Pick anA-diagnosticsissue with a clear reproduction. Improve the error: more accurate span, structured suggestion, better wording. Realistic effort: 20–40 hours including bootstrap, review iterations, UI test churn. - New clippy lint. The clippy issue tracker maintains a queue of "lint requests." Pick one tagged
good-first-issue. Implement, test (UI tests + dogfood the lint against the rustc tree), document. Realistic effort: 30–60 hours. - A new MIR optimization pass (advanced). Choose a narrow, well-bounded transform-e.g., a peephole simplification of a specific MIR pattern. Profile its impact with
rustc-perf. Realistic effort: 60–120 hours and substantial reviewer hand-holding; treat as stretch. rust-analyzerfeature. Implement a code action or completion improvement. RA's architecture is extremely well-documented; the PR loop is fast.
Acceptance criteria¶
- A PR exists, is linked from your portfolio, and has at least one round of review feedback addressed.
- A short write-up (
CAPSTONE_NOTES.md) documenting: what you changed, why, what you learned about the compiler internals, and what reviewers pushed back on. - Your local fork has a working stage-1 build of rustc with your patch applied.
Skills exercised¶
- Months 4 (macros / unsafe), 6.23 (compiler internals).
- The hardening discipline matters less here; the deliverable is upstream code, not a service.
Track 2-High-Performance Fintech: Limit-Order-Book Matching Engine¶
Outcome: a benchmarked, fuzz-tested matching engine for limit and market orders across multiple symbols, single-process, with sub-microsecond p99 hot-path latency on commodity x86_64.
Functional spec¶
- Order types: limit (GTC, IOC, FOK), market, cancel, modify.
- Matching policy: price-time priority. Partial fills allowed. Self-trade prevention configurable.
- Multi-symbol: a
Engineowns N independent symbol books; symbols may be sharded across worker threads. - Wire format: a binary protocol (your design or a subset of FIX/SBE).
- Output: an event stream (
Filled,PartiallyFilled,Cancelled,Rejected,BookUpdate) consumed by downstream feed handlers.
Non-functional spec¶
- Latency: p50 < 200 ns, p99 < 1 µs for the hot path (order in → match → event out), measured under sustained 1 M orders/sec.
- Throughput: ≥ 1 M orders/sec sustained on a single symbol on a single core.
- Determinism: identical inputs produce identical event sequences. No HashMap iteration order in the hot path; use deterministic structures.
- Fault tolerance: panic-safe (
panic = "abort"is acceptable; document operational implications). Persistent log for replay.
Architecture sketch¶
- Hot path is single-threaded per symbol. SPSC ring buffer on input, SPSC on output. Cross-thread coordination only at session boundaries.
- Order book: paired sorted structures (often
BTreeMap<Price, OrderQueue>for asks, mirror for bids). For ultimate latency: array-of-price-levels with sparse bitmap; this is the rabbit hole `Aeron - style designs go down. - Allocator:
mimallocglobal, plus per-symbol bump arenas for short-lived order metadata. - Memory layout:
#[repr(C)]orders, padded to cache-line boundaries; pre-allocated slabs. - No
asyncon the hot path. Async is for session/admin paths only. Mixing the two is the most common architectural mistake in this space.
Test rigor¶
- Property tests:
proptestinvariants on the book-total quantity preserved across fills, no cross of bid > ask except during a match, time priority preserved at a price level. - Fuzz:
cargo-fuzzwith `arbitrary - derived order generators. The corpus must include high-volume sequences with cancels and modifies. - Loom: any cross-thread sync (admin → engine, engine → publisher) must be Loom-verified.
- Bench:
criterionwith regression detection in CI; flamegraphs committed;perf statoutputs (cycles, instructions, IPC, cache-misses) tracked over time.
Hardening pass¶
- LTO fat, codegen-units 1, panic abort, target-cpu=native (for the deployed-on-known-hardware case).
- PGO with a representative replay workload.
- BOLT post-link.
- Deterministic build via Docker pinned to a content hash.
Acceptance criteria¶
- Public repo with the above.
- A README that includes a flamegraph, a
perf stattable, and a latency CDF. - A
THREAT_MODEL.mdcovering the inputs you do and do not validate. - An interview-defensible answer to: "What does your worst-case allocation pattern look like under a 100× burst?"
Skills exercised¶
- Months 3 (concurrency), 4 (unsafe / FFI for the wire codec), 5 (production architecture though the hot path skips most of the hexagonal), 6.21 (custom data structures), 6.22 (allocators).
Track 3-Kernel: A rust-for-linux Character Device¶
Outcome: a working out-of-tree Rust kernel module implementing a non-trivial character device, with KUnit tests, building cleanly against a recent mainline kernel.
Functional spec¶
- A character device (
/dev/<yourname>) that exposes an in-kernel ring buffer. - Operations:
read(drains the ring),write(appends),ioctlfor resize/clear/stats,mmapfor zero-copy access (stretch). - Multi-reader / multi-writer with appropriate kernel synchronization (
SpinLock,Mutexfrom the kernel crate, notstd). - Sysfs entries for runtime tuning.
Why this scope¶
- Touches every cross-FFI surface: char device registration, file operations, copy_from_user/copy_to_user, sysfs, locking.
- Forces you to read kernel-side Rust idioms (
Box::try_new, fallible alloc,Pin<&mut Self>everywhere, Arc-equivalents). - The
rust-for-linuxtoolchain itself is a learning surface: pinned rustc, custom libcore subset, nostd.
Build environment¶
- Linux ≥ 6.8 (Rust support is stable enough for out-of-tree work).
rustup toolchain link kernel <path>to point at the kernel-supported rustc.- A local kernel build with
CONFIG_RUST=y,CONFIG_SAMPLES_RUST=y.
Test rigor¶
- KUnit-based unit tests inside the module.
- `selftest - style scripts running the device through real read/write/ioctl from userspace.
- Stress test: N concurrent readers and writers with
tasksetpinning, watch forKASAN/KCSANreports.
Hardening pass¶
- Kernel-side:
KASAN(kernel address sanitizer),KCSAN(concurrency sanitizer),lockdepenabled in your test kernel. - Module-side: every
unsafeblock carries a// SAFETY:comment justifying the kernel invariants. - A `dmesg - clean run on insertion, exercise, and removal.
Acceptance criteria¶
- The module builds, loads, exercises end-to-end, unloads, with no
KASAN/KCSAN/lockdepwarnings. - A PR-ready patch series formatted for
git format-patch(even if not submitted upstream). - A
KERNEL_NOTES.mddescribing the locking model, the failure modes you considered, and the explicit reason you choseSpinLockvsMutexat each site.
Skills exercised¶
- Months 4 (unsafe + FFI to the kernel C API), 6.22 (
no_std), 6.23 (compiler internals indirectly via the pinned toolchain).
Cross-Track Requirements¶
Regardless of track:
- Hardening workspace integrated. The
hardening/template from Appendix A applies. - Architectural Decision Records (ADRs). At least three for the capstone, each ~1 page.
- Threat model. One page minimum, no matter the track.
- Defense readiness. You should be able to walk a reviewer through the code in 45 minutes and answer "what fails first under load / fuzzing / a malicious input / a pathological kernel state?"
The track choice signals career direction: compiler track for tooling/PL roles, fintech for HFT/exchange/crypto roles, kernel for OS/embedded/security roles. Do not pick based on what looks easiest; pick based on where you want the next interview loop.