Recipe
Write a macro_rules! macro
Use when: a job on Section 1's list (any number of arguments, new syntax, generated items, source text) that you'd otherwise copy and paste
Goal: a macro whose expansion is the code you would have written by hand
Taught in Declarative macros with macro_rules!
Quick recipe
-
1
Write out, as ordinary Rust, the code you want for two different calls.
Example
For
readings!{"bench A" => 21.5}and a three-bench call: a block that makes a map, onemap.insert(..)per bench, thenmap.Why: the macro's output is ordinary Rust; you need to know that code before you can template it.
-
2
Circle the parts that differ between the two expansions. Give each one a name and a fragment specifier.
- the part is a name the macro will define or read —
ident. - the part is a value —
expr(orliteralif only written-out values make sense).
Why: the fixed parts become the transcriber; the varying parts become metavariables.
- the part is a name the macro will define or read —
-
3
Write the call's shape with the metavariables in place. Wrap repeated parts in
$( ... ),*or$( ... ),+.Why: the matcher is the call's grammar;
*or+decides whether an empty call is allowed. -
4
Paste the hand-written expansion, replace the varying parts with the metavariables, and wrap the repeated lines in
$( ... )*. Use{{ }}if the result must be one expression.Why: each repeated variable must appear inside a repetition of the same depth.
-
5
Call it with zero, one and several items. When a result surprises you, read the expansion.
Why: the compiler checks each expansion; an unusual call (the empty one especially) can produce an error or warning the normal calls don't.
Common mistakes
- Using
*when an empty call makes no sense (use+). - Forgetting the inner braces: the expansion is then several statements, not one expression.
- Putting something other than
=>,,or;after anexprvariable.