Chapter 22: Writing your own macros
The introduction explains why writing macros can be useful while warning that their unfamiliar syntax takes practice.
After this chapter you will be able to
- Macro convenience and complexity. Explain why someone might write a macro despite rarely needing to: macros can effectively write code for you, but their syntax differs from ordinary Rust and takes getting used to.
- Macro code generation timing. Explain that a macro takes input and produces code before the compiler examines that generated code.
- Macro rules and compilation. Explain how macro_rules! defines a macro and why an invalid output may cause no error until the macro is called.
- Literal macro input rules. Distinguish a macro's literal input rules from match arms, including what happens when no rule accepts the supplied tokens.
- Captured input fragments. Describe how a macro captures input with a dollar-prefixed name and a fragment specifier, using expr and stmt as examples.
- Repeated token tree inputs. Explain how a macro accepts multiple token trees with repetition syntax, including the separator and the difference between * and +.
- write! argument forwarding. Trace how write! accepts a destination and any number of additional arguments, then passes those arguments through format_args! to write_fmt.
- Repetition and optional commas. Distinguish the *, +, and ? repetition operators, and explain how $(,)? permits a trailing comma.
- matches! patterns and guards. Explain how matches! checks an expression against a pattern and how its optional guard can change the result.
- Shared String holder macro. Explain how a macro taking a type name and length can implement TryFrom for several String-holding types without repeating the implementation, and how it can also declare the types.
What it builds on
- Match statements (chapter 3) · The appearance of a match statement.
- Display and Debug (chapter 1) · Display and Debug formatting used in the examples.
Section by section
Introduction
The introduction explains why writing macros can be useful while warning that their unfamiliar syntax takes practice.
- Macro convenience and complexity: Explain why someone might write a macro despite rarely needing to: macros can effectively write code for you, but their syntax differs from ordinary Rust and takes getting used to.
Why it matters: This sets realistic expectations for when writing a macro may be worth the effort.
Key terms: macros, syntax
22.1 Why macros exist
This section explains why Rust macros are useful for generating code that ordinary functions cannot produce.
- Macro code generation timing: Explain that a macro takes input and produces code before the compiler examines that generated code.
Why it matters: This timing is what lets macros create code for the compiler to handle. - Ordinary function limitation: Explain why an ordinary function cannot generate a collection of new struct definitions with different names, as in the section’s example.
Why it matters: It shows why generating Rust definitions calls for a macro instead of a function.
Key terms: macro, compiler, regular function, structs
22.2 Writing basic macros
This section shows how to define basic Rust macros that match tokens, capture different kinds of input, and handle repeated inputs.
- Macro rules and compilation: Explain how macro_rules! defines a macro and why an invalid output may cause no error until the macro is called.
Why it matters: It explains why defining a macro can succeed even when calling it produces invalid code. - Literal macro input rules: Distinguish a macro's literal input rules from match arms, including what happens when no rule accepts the supplied tokens.
Why it matters: This keeps the match-like appearance of macro rules from giving you the wrong expectations about their inputs and outputs. - Captured input fragments: Describe how a macro captures input with a dollar-prefixed name and a fragment specifier, using expr and stmt as examples.
Why it matters: Fragment specifiers let a macro accept useful categories of Rust input instead of only fixed tokens. - Repeated token tree inputs: Explain how a macro accepts multiple token trees with repetition syntax, including the separator and the difference between * and +.
Why it matters: Repetition syntax is what lets a macro accept a list of inputs rather than just one captured item.
Key terms: macro_rules!, token parser, token, match statement, no rules expected the token, $input:expr, fragment specifiers, expr, stmt, ident
22.3 Reading macros from the standard library
The section reads standard-library macros to show how their input patterns, recursive calls, and optional syntax explain what the macros accept and do.
- write! argument forwarding: Trace how write! accepts a destination and any number of additional arguments, then passes those arguments through format_args! to write_fmt.
Why it matters: Reading the input pattern and forwarded arguments helps you understand a macro without treating it as a black box. - todo! input alternatives: Describe the two kinds of input shown for todo! and what its arms do with them.
Why it matters: A macro's arms can reveal useful forms of a familiar macro that you may not have tried. - Recursive macro calls: Explain how a macro arm can call the same macro again, using the section’s example or dbg! to illustrate.
Why it matters: Recognizing a self-call lets you follow which macro arm handles an input next. - Repetition and optional commas: Distinguish the *, +, and ? repetition operators, and explain how $(,)? permits a trailing comma.
Why it matters: These operators let you tell which variations of an input a macro accepts. - matches! patterns and guards: Explain how matches! checks an expression against a pattern and how its optional guard can change the result.
Why it matters: The guard explains why a matches! check can return false even when its pattern fits.
Key terms: write!, $dst, $($arg:tt)*, write_fmt, format_args!, todo!, panic!, token trees, macro arms, my_macro!
22.4 Using macros to keep your code clean
Shows how a macro can remove repeated code for String-holding types, while cautioning when writing a macro is worthwhile.
- Shared String holder macro: Explain how a macro taking a type name and length can implement TryFrom for several String-holding types without repeating the implementation, and how it can also declare the types.
Why it matters: This example shows how a macro can keep repeated type and trait code in one place. - When macros are worthwhile: Justify using a macro for this repeated code while recognizing the section’s caution that macros are complicated and are usually best reserved for work a simple function cannot do well.
Why it matters: It helps you decide whether removing repetition is worth the complexity of a macro.
Key terms: macro, TryFrom, String, type name, length, simple function, repetitive code
22.5 Summary
Concepts explained in this chapter
- macroA Rust macro generates or transforms source code during compilation.
- macro repetitionMacro repetition is declarative macro syntax for matching or expanding zero or more or one or more repeated token sequences with optional separators.
- fragment specifierA fragment specifier is a macro pattern annotation that declares what syntactic category of token input a metavariable can match.
- declarative macromacro_rules! is Rust syntax for defining declarative macros by matching token sequences and expanding them into source code.
- expression fragment specifierAn expression fragment specifier matches a Rust expression as macro input.
- identifier fragment specifierAn identifier fragment specifier matches a Rust identifier such as a variable or function name as macro input.
- tokenA token is a lexical unit of source input, such as a number, identifier, punctuation mark, or other syntactic element.
- token-tree fragment specifierA token-tree fragment specifier matches a token tree, allowing a broad range of syntactic input in a macro pattern.
- dbg! macroThe dbg! macro evaluates an expression, prints its source and value for debugging, and returns the value.
- guardA match guard is an additional Boolean condition attached to a Rust match arm after its pattern matches.
- macro recursionMacro recursion is a macro-expansion technique in which a macro invokes itself to process or handle additional input.
- todo!todo! is a Rust standard-library macro that marks unfinished code and panics if the marked code is executed.
- token treeA token tree is a syntactic token or delimited group of tokens that can be matched and repeated by a Rust macro.
- TryFromTryFrom is a Rust trait for fallible conversion between types that may fail for some input values.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
