Saltar a contenido

Week 16 - Procedural Macros

16.1 Conceptual Core

  • Procedural macros are compiled crates of type proc-macro that take TokenStream in and return TokenStream out. Three flavors:
  • #[derive(MyTrait)]-extends a struct/enum with trait impls.
  • Attribute macros (#[my_attr])-wholesale rewrite of an item.
  • Function-like (my_macro!(...))-like macro_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 via quote! {}).

16.2 Mechanical Detail

  • Spans: every token carries a Span recording 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 use Span::mixed_site() for hygiene-sensitive identifiers.
  • Diagnostics: stable proc macros emit errors via compile_error! token streams. Nightly has proc_macro::Diagnostic with structured suggestions (used by serde on nightly).
  • Testing: trybuild for compile-fail tests; cargo expand for snapshot tests (insta works 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-gate proc-macro2's nightly feature for span fidelity.

16.5 Production Hardening Slice

  • Add trybuild UI tests. Pin the toolchain (UI test snapshots are toolchain-sensitive). Add cargo expand snapshot tests via insta. 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).

Comments