A procedural macro is Rust code that receives a TokenStream and returns a TokenStream. Rust expands that returned stream into compiler-checked code, making procedural macros useful for generating code that normal functions cannot generate. section 1.3.1 section 1.6
Use a procedural macro when declarative macros are no longer expressive enough—for example, when existing structs need manipulation or generated behavior must depend on detailed input syntax. They add power, but also setup, expansion complexity, and less reliable IDE assistance when expansion fails. section 3.0 section 1.3.3
Rust provides three kinds of procedural macro:
- Derive macros run from #[derive(...)] on a struct, enum, or union. They leave the annotated type intact and add code such as trait implementations or functions. section 1.3.1
- Attribute macros define custom attributes and replace the item they decorate, allowing transformations of types, traits, or functions. section 1.3.1
- Function-like macros are the remaining procedural-macro form, alongside derive and attribute macros. section 1.6
A minimal derive macro
A Hello derive macro can accept the tokens for struct Example and generate a hardcoded method. Put the macro in a library with proc-macro = true, mark its entry function with #[proc_macro_derive(Hello)], then invoke it as #[derive(Hello)]. The function receives and returns TokenStream; quote helps construct generated output and syn helps parse input. section 3.1 section 3.2 section 3.9
Generated code needs testing from a separate application, not only cargo check in the macro library. Inspect expansions with cargo expand, and use compiler errors to locate invalid generated syntax at the macro invocation. section 3.2 section 3.9
Figure 1.2 The types of macros in Rust
Compare derive, attribute, and function-like macros visually before choosing how generated code should attach to application code. section 1.3.1

