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 周前