---
title: Declarative Macros (macro_rules!)
url: https://doc.liz6.com/en/rust/08-macro-system/01-declarative-macros-macro-rules
locale: en
area: rust
tags:
- rust
- macro-system
date: 2026-06-30
modified: 2026-07-16
description: 'macro_rules! defines macros using pattern matching and repetition—$($x:expr),* represents zero or more comma-separated expressions. Fragment specifiers (expr/ty/ident/tt) restrict the types of syntax fragments matched. The tt muncher is an advanced technique for implementing variadic macros. The difference between declarative macros and functions: macros expand into AST nodes at compile time, while functions are called at runtime—macros enable syntactic abstractions that functions cannot achieve.'
---

# Declarative Macros (`macro_rules!`)

> `macro_rules!` defines macros using pattern matching and repetition—`$($x:expr),*` represents zero or more comma-separated expressions. Fragment specifiers (`expr`/`ty`/`ident`/`tt`) restrict the types of syntax fragments matched. The tt muncher is an advanced technique for implementing variadic macros. The difference between declarative macros and functions: macros expand into AST nodes at compile time, while functions are called at runtime—macros enable syntactic abstractions that functions cannot achieve.

## `macro_rules!` is Not a Function

```rust
macro_rules! vec {
    ( $( $x:expr ),* ) => {                                    // Match pattern
        {
            let mut temp_vec = Vec::new();
            $( temp_vec.push($x); )*                             // Expansion pattern
            temp_vec
        }
    };
}
```

The fundamental difference between macros and functions: macros **expand into code at compile time** (they are not called at runtime). This means macros can accept a variable number of arguments, generate code of different types, and generate identifiers—capabilities that functions do not possess.

## Fragment Specifiers: The Macro's "Type System"

| specifier | matches | example |
|-----------|---------|---------|
| `:expr` | any expression | `42`, `x + y`, `{ ... }` |
| `:ident` | identifier | `my_var`, `HashMap` |
| `:ty` | type | `i32`, `Vec<String>` |
| `:tt` | any token tree | most general, used for building advanced macros |
| `:literal` | literal | `1`, `"hello"`, `true` |
| `:pat` | pattern | `Some(x)`, `1..=10` |
| `:path` | path | `std::collections::HashMap` |
| `:block` | block | `{ stmt1; stmt2; }` |
| `:stmt` | statement | `let x = 1;`, `x;` |
| `:item` | item | `fn f() {}`, `struct S;` |
| `:meta` | attribute content | `derive(Debug)` |

## Repetition Patterns

```rust
$( $key:expr => $value:expr ),*    $(,)?       # Optional trailing comma
```

- `*`: zero or more times
- `+`: one or more times
- `?`: zero or one time (optional)

## tt Muncher: Recursively Processing Token Trees

The most powerful macro pattern—uses recursion and `:tt` to handle arbitrarily complex input:

```rust
macro_rules! html {
    ($text:expr) => { format!("{}", $text) };
    (<$tag:ident> $($children:tt)* </$tag:ident>) => {
        format!("<{}>{}</{}>", stringify!($tag),
                html!($($children)*),            // Recursively expand children!
                stringify!($tag))
    };
}
let s = html!(<div> "hello" <span> "world" </span> </div>);
// → "<div>hello<span>world</span></div>"
```

The principle of the tt muncher: each macro invocation matches the `<tag> ... </tag>` pattern, then recursively expands `$children`. This does not require a proc macro—it can be implemented purely with `macro_rules!`.

## References

- **Little Book of Rust Macros**: danielkeep.github.io/tlborm
- **Rust Book**: Chapter 19.5 — Macros

*Keywords: macro_rules!, declarative macro, repetition, tt muncher, fragment specifier, compile-time expansion*
