6 min read #rust #Macro System
On this page

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

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"

specifiermatchesexample
:exprany expression42, x + y, { ... }
:identidentifiermy_var, HashMap
:tytypei32, Vec<String>
:ttany token treemost general, used for building advanced macros
:literalliteral1, "hello", true
:patpatternSome(x), 1..=10
:pathpathstd::collections::HashMap
:blockblock{ stmt1; stmt2; }
:stmtstatementlet x = 1;, x;
:itemitemfn f() {}, struct S;
:metaattribute contentderive(Debug)

Repetition Patterns

$( $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:

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