---
title: 声明宏 (macro_rules!)
url: https://doc.liz6.com/rust/08-macro-system/01-declarative-macros-macro-rules
locale: zh
area: rust
tags:
- rust
- 宏系统
date: 2026-06-30
modified: 2026-06-30
description: macro_rules! 用模式匹配+重复来定义宏——$($x:expr),* 表示零或多个用逗号分隔的表达式。fragment specifiers(expr/ty/ident/tt)限制匹配的语法片段类型,tt muncher 是实现变参宏的高级技巧。声明宏和函数的区别:宏在编译期展开为 AST 节点,函数在运行…
---

# 声明宏 (macro_rules!)

> `macro_rules!` 用模式匹配+重复来定义宏——`$($x:expr),*` 表示零或多个用逗号分隔的表达式。fragment specifiers(expr/ty/ident/tt)限制匹配的语法片段类型,tt muncher 是实现变参宏的高级技巧。声明宏和函数的区别:宏在编译期展开为 AST 节点,函数在运行时被调用——宏可以做到函数做不到的语法级抽象。

## macro_rules! 不是函数

```rust
macro_rules! vec {
    ( $( $x:expr ),* ) => {                                    // 匹配模式
        {
            let mut temp_vec = Vec::new();
            $( temp_vec.push($x); )*                             // 展开模式
            temp_vec
        }
    };
}
```

宏和函数的根本区别：宏在**编译期展开为代码**（不是运行时调用）。这意味着宏可以接受变长参数、可以产生不同类型的代码、可以生成标识符——这些都是函数做不到的。

## Fragment Specifiers: 宏的"类型系统"

| specifier | 匹配 | 示例 |
|-----------|------|------|
| `:expr` | 任意表达式 | `42`, `x + y`, `{ ... }` |
| `:ident` | 标识符 | `my_var`, `HashMap` |
| `:ty` | 类型 | `i32`, `Vec<String>` |
| `:tt` | 任意 token tree | 最通用，用于构建高级宏 |
| `:literal` | 字面量 | `1`, `"hello"`, `true` |
| `:pat` | 模式 | `Some(x)`, `1..=10` |
| `:path` | 路径 | `std::collections::HashMap` |
| `:block` | 块 | `{ stmt1; stmt2; }` |
| `:stmt` | 语句 | `let x = 1;`, `x;` |
| `:item` | 项 | `fn f() {}`, `struct S;` |
| `:meta` | 属性内容 | `derive(Debug)` |

## 重复模式

```rust
$( $key:expr => $value:expr ),*    $(,)?       # 可选的尾随逗号
```

- `*`: 零次或多次
- `+`: 一次或多次
- `?`: 零次或一次（可选）

## tt Muncher: 递归处理 token 树

最强大的宏模式——用递归和 `:tt` 处理任意复杂的输入：

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

tt muncher 的原理：每次宏调用时，匹配 `<tag> ... </tag>` 模式，然后递归展开 `$children`。这不需要 proc macro——纯 macro_rules! 即可实现。

## 参考

- **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*
