liveBook · Manning

Procedural macro

As explained in Write Powerful Rust Macros

A procedural macro is a Rust macro implemented as code that receives a token stream and programmatically produces another token stream.

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
figure

Compare derive, attribute, and function-like macros visually before choosing how generated code should attach to application code. section 1.3.1

How Write Powerful Rust Macros covers it

How deep it goes
Explains TokenStream input and output, lexing, parsing, setup, testing, and expansion inspection without claiming full compiler internals.
Examples
Builds a Hello derive macro that adds behavior to struct Example through a macro library and consumer application.
What it assumes
Assumes working Rust and Cargo familiarity while incrementally teaching procedural-macro setup and TokenStream handling.
What you can do after
Build it from scratch as a derive-macro library, test generated code, and inspect expansions with cargo expand.
How it connects
Connects procedural macros to metaprogramming, declarative macros, TokenStream, abstract syntax trees, syn, quote, and cargo expand.

Also explained in

Compare how 2 books explain procedural macro →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Write Powerful Rust Macros. The section links point to the parts of the book it draws on.