Rust 工程化实战:用 macro_rules 写零成本宏消除样板代码
test12026-09-251 次阅读
宏解决什么问题
Rust 的泛型擅长类型层面的复用,但有些重复不是类型参数能消掉的:比如为多个结构体实现同一套 trait、构造一组同名测试、按固定模式打印日志。macro_rules! 是声明式宏,在编译期展开成代码,零运行期开销,且能跨多种语法结构复用。
第一个宏:自动实现 Debug 风格摘要
假设想给一批结构体快速加一个 summary 方法,返回字段名与值拼接的字符串:
macro_rules! impl_summary {
($name:ident { $($field:ident),* }) => {
impl $name {
pub fn summary(&self) -> String {
let mut s = String::from(stringify!($name));
$(
s.push_str(&format!(" {}={}", stringify!($field), self.$field));
)*
s
}
}
};
}
struct User { id: u64, name: String }
struct Order { id: u64, amount: f64 }
impl_summary!(User { id, name });
impl_summary!(Order { id, amount });
变长参数宏:批量构造测试
下面这个宏给一组输入一次性生成测试用例,省掉逐个 fn 的样板:
macro_rules! test_cases {
($($name:ident: $input:expr => $expect:expr),* $(,)?) => {
$(
#[test]
fn $name() {
assert_eq!(process($input), $expect);
}
)*
};
}
test_cases! {
t_empty: "" => 0,
t_single: "a" => 1,
t_multi: "a b c" => 3,
}
调试宏展开
不确定宏生成了什么代码时,用编译器选项把它.dump 出来:
cargo expand # 需要安装 cargo-expand
# 或
rustc -Zunpretty=expanded main.rs
使用准则
- 宏的匹配臂从左到右尝试,越具体的规则放越前面,避免被宽泛规则抢先匹配。
- 重复段 $(...) 的 separators 要写全,比如 $(,)? 允许结尾逗号,能让调用处更宽容。
- 宏能隐藏控制流,可读性差于普通函数,仅在"确实跨类型/跨结构重复"时使用,别用来炫技。
- hygiene(卫生性)让宏内生成的变量不会和调用处冲突,但跨宏传递标识符(ident)时要注意命名意图清晰。
macro_rules! 是 Rust 工程里削减样板的主力工具:编译期展开、零运行时成本、能覆盖泛型触及不到的语法级重复。用得好,既保住类型安全,又把重复代码压到最低。
T
test1
文章作者
宏解决什么问题 Rust 的泛型擅长类型层面的复用,但有些重复不是类型参数能消掉的:比如为多个结构体实现同一套 tra...
- 分类
- 技术
- 发布时间
- 2026-09-25
- 字数
- 约 1513 字
- 阅读
- 1 次
