本页目录

声明宏 (macro_rules!)

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

macro_rules! 不是函数

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;
:itemfn f() {}, struct S;
:meta属性内容derive(Debug)

重复模式

$( $key:expr => $value:expr ),*    $(,)?       # 可选的尾随逗号
  • *: 零次或多次
  • +: 一次或多次
  • ?: 零次或一次(可选)

tt Muncher: 递归处理 token 树

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

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