liveBook · Manning

Macro expansion

As explained in Write Powerful Rust Macros

Macro expansion is the process of replacing a macro invocation with the source code generated or transformed by that macro.

Macro expansion replaces a macro invocation with generated or transformed Rust code at compile time. The resulting structs, functions, and other ordinary code can then be linted, type-checked, compiled, or turned into a runnable binary. section 1.2

A declarative macro expansion proceeds through a small matching-and-replacement cycle:

  • Rust recognizes syntax such as my_vec!() as a macro invocation and searches the available standard-library or custom macros. section 2.1.3
  • The macro checks matchers from top to bottom and selects the first matcher that fits the invocation. section 2.1.3
  • The transcriber supplies replacement code, which is inserted where the invocation appeared. section 2.1.3
  • Only the invocation is replaced; surrounding syntax such as a statement’s semicolon remains in place. section 2.1.3

Expanding an empty vector macro

Given the matcher () => [ Vec::new() ];, the call let empty: Vec<i32> = my_vec!(); becomes let empty: Vec<i32> = Vec::new(); before checking, linting, or running the application. The transcriber does not need to add a semicolon because the original statement already supplies it. section 2.1.3

Use cargo expand to inspect application code after expansion. It can print generated code even when the output is invalid, helping locate compilation problems; piping the result to a file can support IDE-based investigation. section 3.5

Figure 1.1 I contain multitudes! How our simple example hides many more lines of code, generated by macros.
figure

A visual overview of ordinary Rust code alongside the additional lines generated by macro expansion. section 1.2

How Write Powerful Rust Macros covers it

How deep it goes
Traces matcher selection, transcriber insertion, preserved punctuation, and expansion inspection, while stopping short of procedural-macro internals.
Examples
Uses my_vec!() expanding to Vec::new(), derive-generated implementations, and cargo expand output for debugging.
What it assumes
Assumes basic Rust syntax and compiler workflow while introducing macro invocation, matchers, transcribers, and generated code.
What you can do after
Diagnose macro-generated code with cargo expand, including invalid output and errors originating in generated functions.
How it connects
Connects macro expansion with metaprogramming, declarative macros, procedural macros, compilation, type checking, and IDE support.

Also explained in

Compare how 4 books explain macro expansion →

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.