调试宏
expand! 宏
rust
▶ Run// 使用 cargo expand 查看宏展开
cargo install cargo-expand
cargo expand
// 查看特定文件的宏展开
cargo expand my_crate::maintrace_macros!
rust
▶ Run// 启用宏追踪(需要 nightly)
#![feature(trace_macros)]
fn main() {
trace_macros!(true);
factorial!(5);
trace_macros!(false);
}macro_rules! 调试技巧
rust
▶ Runmacro_rules! debug_macro {
($($tt:tt)*) => {{
// 打印原始 tokens
println!("输入:{}", stringify!($($tt)*));
// 继续正常处理
// ...
}};
}完整示例
示例 1:日志宏
rust
▶ Runmacro_rules! log {
($level:ident, $($arg:tt)*) => {
{
use std::time::SystemTime;
let timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
println!("[{}][{}] {}", timestamp, stringify!($level), format!($($arg)*));
}
};
}
macro_rules! info {
($($arg:tt)*) => {
log!(INFO, $($arg)*)
};
}
macro_rules! error {
($($arg:tt)*) => {
log!(ERROR, $($arg)*)
};
}
macro_rules! debug {
($($arg:tt)*) => {
log!(DEBUG, $($arg)*)
};
}
fn main() {
info!("Application started");
error!("Something went wrong: {}", "error message");
debug!("Debug info: {:?}", vec![1, 2, 3]);
}示例 2:HashMap 字面量
rust
▶ Runmacro_rules! hash_map {
($($key:expr => $value:expr),* $(,)?) => {{
use std::collections::HashMap;
let mut map = HashMap::new();
$(
map.insert($key, $value);
)*
map
}};
}
fn main() {
let map = hash_map! {
"a" => 1,
"b" => 2,
"c" => 3,
};
println!("{:?}", map);
}示例 3:SQL 查询构建
rust
▶ Runmacro_rules! select {
($columns:tt FROM $table:ident WHERE $where:tt) => {
{
// 这里可以生成 SQL 字符串
// 或者使用过程宏生成真正的查询代码
format!(
"SELECT {} FROM {} WHERE {}",
stringify!($columns),
stringify!($table),
stringify!($where)
)
}
};
}
fn main() {
let query = select!(id, name FROM users WHERE id = 1);
println!("{}", query);
// SELECT id, name FROM users WHERE id = 1
}常见错误
错误 1:优先级问题
rust
▶ Runmacro_rules! double {
($e:expr) => {
$e * $e // ❌ 优先级问题
};
}
fn main() {
// 展开:2 + 3 * 2 + 3 = 11(不是 25)
let x = double!(2 + 3);
}
// ✅ 修复
macro_rules! double_fixed {
($e:expr) => {
($e) * ($e)
};
}错误 2:变量捕获
rust
▶ Runmacro_rules! bad_macro {
($x:expr) => {
let x = $x; // ❌ 可能捕获意外的 x
};
}
// ✅ 使用唯一变量名
macro_rules! good_macro {
($x:expr) => {
let __macro_x = $x;
};
}错误 3:片段类型错误
rust
▶ Runmacro_rules! wrong_type {
($x:ident) => { /* ... */ };
}
fn main() {
// ❌ 错误:123 不是 ident
// wrong_type!(123);
// ✅ 正确
wrong_type!(my_var);
}宏最佳实践
┌─────────────────────────────────────────────────────┐
│ 宏最佳实践 │
├─────────────────────────────────────────────────────┤
│ │
│ 1. 优先使用函数 │
│ • 函数更容易理解和调试 │
│ • 只在必要时使用宏 │
│ │
│ 2. 遵循 Rust 语法 │
│ • 让宏调用看起来像普通 Rust 代码 │
│ • 使用熟悉的语法结构 │
│ │
│ 3. 提供良好文档 │
│ • 说明宏的用法 │
│ • 提供示例 │
│ │
│ 4. 注意卫生 │
│ • 避免变量名冲突 │
│ • 使用唯一内部变量名 │
│ │
│ 5. 测试宏 │
│ • 测试各种输入情况 │
│ • 测试边界情况 │
│ │
└─────────────────────────────────────────────────────┘练习
练习 1:Getter/Setter 宏
创建一个宏,为结构体字段生成 getter 和 setter 方法:
rust
▶ Rungenerate_accessors! {
struct Person {
name: String,
age: u32,
}
}练习 2:断言宏
实现一个增强版断言宏,显示更多调试信息:
rust
▶ Runassert_with_info!(x > 0, "x 必须是正数", x = x);练习 3:简化错误处理
创建一个简化错误处理的宏:
rust
▶ Runlet file = try_or_return!(File::open("test.txt"));小结
本章我们学习了:
- ✅ macro_rules! 语法
- ✅ 宏片段类型
- ✅ 重复匹配
- ✅ 内置宏
- ✅ 递归宏
- ✅ 过程宏基础
- ✅ 调试宏的技巧
常用内置宏速查
| 宏 | 用途 |
|---|---|
stringify! | 转 token 为字符串 |
concat! | 拼接字符串 |
env! | 获取环境变量 |
include! | 包含文件 |
compile_error! | 编译错误 |
line! / file! | 行号/文件名 |
module_path! | 模块路径 |
format_args! | 格式化参数 |