Macros with macro_rules!

In Rust a macro_rules! macro is a list of rules. Each rule has a matcher, which describes the tokens it accepts, and a transcriber, which is the code it produces. The compiler expands the macro before type checking, so the result is ordinary Rust that can use any syntax the macro writes.

Coming from C++? Macros and templates prints a mixed list of arguments with a variadic template and with a macro, and then calls a one-argument macro with two values: C++ accepts the template, Rust rejects the call.

This lesson writes one rule, several rules, a macro with hygiene and one that builds a value. The next lesson adds repetition, which is what makes a macro variadic.

A macro with one rule

macro_rules! square has one rule: an expression in parentheses, written $value:expr, becomes the expression multiplied by itself. The dollar sign marks a metavariable, and the name after the colon is its kind.

The program calls the macro with four and prints sixteen. The expansion happens at compile time, and the binary contains the multiplication, not a function call.

macro_rules! square { ($value:expr) => { $value * $value }; } fn main() { println!("{}", square!(4)); }

rustc --edition 2024 simple.rs
./simple

16

The macro must be defined before the call in the same file, which the last section turns into an error. The lesson on repetition extends this one to any number of arguments.

Several matchers

A macro may have several rules, and the first matcher that fits wins. The rules below take the word number or the word word before the value, so the macro reads like a small language.

The program calls both forms and prints the two strings. The matcher sees the literal word, and the transcriber uses the expression that follows it.

macro_rules! describe { (number $value:expr) => { format!("number {value}", value = $value) }; (word $value:expr) => { format!("word {value}", value = $value) }; } fn main() { println!("{}", describe!(number 7)); println!("{}", describe!(word "rust")); }

rustc --edition 2024 patterns.rs
./patterns

number 7
word rust

The order of the rules matters, because a general matcher above a specific one would swallow it. The compiler reports the first rule that could not match when a call fails, which is the debugger of macro code.

Hygiene

Names created inside a macro are not visible outside it, and names outside are not captured by accident. The macro below uses a local result and the program also has one; the two do not collide, which is what hygiene means.

The program prints the outer result and the doubled value. The macro expression is wrapped in a block so that its local binding stays inside.

macro_rules! double { ($value:expr) => {{ let result = $value * 2; result }}; } fn main() { let result = 100; let doubled = double!(5); println!("{result} {doubled}"); }

rustc --edition 2024 hygiene.rs
./hygiene

100 10

A macro that expands to statements needs a block, which is why the transcriber above has two pairs of braces: the outer pair is the rule, the inner pair the block. An expression used in a binding position must not end with a semicolon, or the value becomes the unit type, which is the same rule the lesson on functions applies to every block. Hygiene is why a macro can use a short name without breaking the caller. The exception is a metavariable, which deliberately refers to the caller expression, as $value does here.

A macro that builds a value

A macro may expand to a struct literal, which is how a short constructor is written without a function. The transcriber names the struct and the two fields, and the metavariables fill the values.

The program builds a point with the macro and prints its fields. The struct is declared after the macro, because the expansion only needs it at the call site.

macro_rules! point { ($x:expr, $y:expr) => { Point { x: $x, y: $y } }; } struct Point { x: i32, y: i32, } fn main() { let point = point!(3, 4); println!("{} {}", point.x, point.y); }

rustc --edition 2024 struct_value.rs
./struct_value

3 4

The associated function Point::new from the lesson on methods is the other way to get the same call shape. A macro is worth it when the arguments are not values but syntax.

A macro used too early

A macro is visible from its definition to the end of the module, so a call above it cannot find it. The error says so, and the note points at the definition below and suggests moving it.

The program calls later! before the declaration. The compiler reports the name, the call and the definition, which is a complete fix in one message.

fn main() { println!("{}", later!(2)); } macro_rules! later { ($value:expr) => { $value + 1 }; }

rustc --edition 2024 order_err.rs

error: cannot find macro `later` in this scope
--> order_err.rs:2:20
|
2 | println!("{}", later!(2));
| ^^^^^ consider moving the definition of `later` before this call
|
note: a macro with the same name exists, but it appears later
--> order_err.rs:5:14
|
5 | macro_rules! later {
| ^^^^^

warning: unused macro definition: `later`
--> order_err.rs:5:14
|
5 | macro_rules! later {
| ^^^^^
|
= note: `#[warn(unused_macros)]` (part of `#[warn(unused)]`) on by default

error: aborting due to 1 previous error; 1 warning emitted

Functions do not have this rule, because the whole module is visible to them. A macro with #[macro_export] is exported at the crate root and can be used from other crates, which the lesson on procedural macros compares.

Exercises

  1. Write a macro that returns the smaller of two expressions. Which matcher and transcriber do you need?
  2. Add a rule to the patterns macro that takes a value with no keyword and formats it differently.
  3. Move the definition of the last macro above main and check that the program compiles.

Questions

A new set of questions in a new order every time. Choose one answer for each, then check.

  1. What are the two parts of a macro rule?

    Answer

    A matcher and a transcriber

    The matcher describes the tokens accepted and the transcriber writes the expansion.

  2. What does $value:expr mean in a matcher?

    Answer

    A metavariable that matches one expression

    The dollar marks a metavariable, and expr is the kind of token it accepts.

  3. What does macro hygiene prevent?

    Answer

    Names inside the macro colliding with names at the call site

    Names introduced by the macro are in their own scope, so the caller is unaffected.

  4. When is a macro visible?

    Answer

    From its definition to the end of the module

    A macro must be defined before the call, unlike a function.

  5. What does this program print? macro_rules! square { ($value:expr) => { $value * $value }; } fn main() { println!("{}", square!(4)); }

    Answer

    16

    The macro expands to four times four, and the print shows sixteen.

  6. What does this program print? macro_rules! describe { (number $value:expr) => { format!("number {value}", value = $value) }; (word $value:expr) => { format!("word {value}", value = $value) }; } fn main() { println!("{}", describe!(number 7)); println!("{}", describe!(word "rust")); }

    Answer

    number 7 / word rust

    Each call matches the rule with its keyword and formats the value after it.

  7. What does this program print? macro_rules! double { ($value:expr) => {{ let result = $value * 2; result }}; } fn main() { let result = 100; let doubled = double!(5); println!("{result} {doubled}"); }

    Answer

    100 10

    Hygiene keeps the macro result separate from the outer one, so the two values are 100 and 10.

  8. What does this program print? macro_rules! point { ($x:expr, $y:expr) => { Point { x: $x, y: $y } }; } struct Point { x: i32, y: i32, } fn main() { let point = point!(3, 4); println!("{} {}", point.x, point.y); }

    Answer

    3 4

    The expansion builds the literal at the call site, where the struct is already declared.

  9. What is the first line of the compiler output for this program? fn main() { println!("{}", later!(2)); } macro_rules! later { ($value:expr) => { $value + 1 }; }

    Answer

    error: cannot find macro later in this scope

    The macro is not in scope before its definition, and the note points at the definition below.

  10. What does this program print? macro_rules! twice { ($value:expr) => { $value * 2 }; } fn main() { let value = 5; println!("{} {}", twice!(value + 1), twice!(3)); }

    Answer

    12 6

    The first argument is six before doubling, and the second is three, so the outputs are twelve and six.

The code on this page was built with rustc 1.96.1 and run on Alpine Linux 3.24.3 (x86_64) in a Docker container.

Article author: Arthur Isaev