Macros

Procedural Macros

A procedural macro (proc-macro) is a compiled Rust function that executes at compile time inside the compiler process, receiving an input proc_macro::TokenStream and returning a generated proc_macro::TokenStream.

Why Procedural Macros Require a Dedicated proc-macro Crate

Because procedural macros are compiled into native machine code (a dynamic library / .dylib / .so) and loaded by rustc to run while compiling downstream crates, Cargo requires procedural macros to live in their own crate with proc-macro = true in Cargo.toml:

[package]
name = "my_macros"
version = "0.1.0"
edition = "2024"

[lib]
proc-macro = true

[dependencies]
syn = "2"
quote = "1"
proc-macro2 = "1"
+----------------------------------+
|     my_macros (proc-macro = true)|
|  Compiled for host architecture  |
+-----------------+----------------+
                  │  Loaded by `rustc` at compile time
                  ▼
+----------------------------------+
|     app_crate (normal crate)     |
|  #[derive(MyTrait)] struct Foo;  | ──(TokenStream)──> my_macros ──(Generated impl)──> AST
+----------------------------------+

The Three Kinds of Procedural Macros

Procedural Macro Kind Attribute on Definition Usage Syntax Input TokenStream(s) Output TokenStream
1. Derive Macro #[proc_macro_derive(Name, attributes(helper))] #[derive(Name)] on a struct, enum, or union 1 (item: the struct/enum tokens) Appended after the original struct/enum (never modifies original item)
2. Attribute-Like Macro #[proc_macro_attribute] #[my_attr(args)] on any item (fn, struct, impl, mod) 2 (attr: attribute args, item: the target item) Replaces the target item entirely
3. Function-Like Macro #[proc_macro] my_macro!(tokens) 1 (input: tokens inside (...)) Replaces the macro invocation at the call site

The Standard Proc-Macro Ecosystem (proc-macro2, syn, quote)

While the compiler's built-in proc_macro crate provides raw TokenTrees (Group, Ident, Punct, Literal), production procedural macros almost always combine three foundational crates:

  1. syn: Parses a raw TokenStream into strongly typed Rust AST structs (DeriveInput, ItemFn, Expr).
  2. quote: Provides the quote! { ... } quasi-quoting macro that turns Rust syntax with #variable interpolations back into a TokenStream.
  3. proc-macro2: A wrapper around proc_macro::TokenStream that can be used inside unit tests and regular non-proc-macro library code.