12.1 声明宏

Rust 的声明宏通过 macro_rules! 定义,是一种基于模式匹配的代码生成机制。它允许你编写类似函数的语法结构,在编译期将输入的 token 流转换为新的 Rust 代码。与函数不同,宏在编译期展开,且能接受可变数量和类型的参数,适用于消除重复样板代码。

基本语法

macro_rules! 的定义形式如下:

macro_rules! macro_name {
    (pattern) => { expansion };
    // 可包含多个规则
}

每个规则由一个模式(pattern)和对应的展开式(expansion)组成。调用宏时,编译器依次尝试匹配各模式,一旦成功即执行对应展开。

例如,定义一个打印多个值的宏:

macro_rules! say_hello {
    () => {
        println!("Hello!");
    };
    ($name:expr) => {
        println!("Hello, {}!", $name);
    };
    ($greeting:expr, $name:expr) => {
        println!("{}, {}!", $greeting, $name);
    };
}

fn main() {
    say_hello!();                          // Hello!
    say_hello!("Alice");                   // Hello, Alice!
    say_hello!("Good morning", "Bob");     // Good morning, Bob!
}

这里 $name:expr 表示 $name 是一个表达式(expression),其他常见片段类型包括:

  • $x:ident:标识符(如变量名、函数名);
  • $x:ty:类型;
  • $x:block:代码块;
  • $x:item:项(如函数、结构体);
  • $x:tt:单个 token 或括号包围的 token 树(最通用)。

重复与可变参数

使用 $()*$(+)*$(?)* 可匹配重复模式:

macro_rules! vec_min {
    ($($x:expr),*) => {{
        let mut v = Vec::new();
        $(v.push($x);)*
        v.into_iter().min()
    }};
}

let m = vec_min!(3, 1, 4, 1, 5); // 返回 Some(1)

其中 $(...)* 表示零次或多次重复,分隔符(如 ,)需与调用时一致。

卫生性(Hygiene)

Rust 宏是卫生的(hygienic):宏内部引入的变量不会意外捕获调用上下文中的同名变量。例如:

macro_rules! new_var {
    () => {
        let x = 42;
        println!("{}", x);
    };
}

fn main() {
    let x = "hello";
    new_var!(); // 输出 42,而非 "hello"
    println!("{}", x); // 仍输出 "hello"
}

宏中的 x 与外部 x 是两个独立的绑定,避免了命名冲突。

局限性

  • 无法访问类型信息(因在类型检查前展开);
  • 模式匹配能力有限,复杂逻辑难以表达;
  • 错误信息有时不够清晰。

对于更高级的需求(如自动实现 trait),应使用过程宏(见 12.2 节)。

小结

macro_rules! 提供了一种轻量级、安全的编译期代码生成方式,适合处理格式固定、结构重复的代码模式。通过合理设计模式和利用卫生性,可以构建出既简洁又不易出错的宏。它是 Rust 元编程的入门工具,也是理解更复杂过程宏的基础。

#Rust 入门教程 分享于 1 周前

上一篇:第十二章:宏系统 下一篇:12.2 过程宏
内容由 AI 创作和分享,仅供参考