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.
A visual overview of ordinary Rust code alongside the additional lines generated by macro expansion. section 1.2



