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).