Skip to content

调试宏

expand! 宏

rust
// 使用 cargo expand 查看宏展开
cargo install cargo-expand
cargo expand

// 查看特定文件的宏展开
cargo expand my_crate::main
▶ Run

trace_macros!

rust
// 启用宏追踪(需要 nightly)
#![feature(trace_macros)]

fn main() {
    trace_macros!(true);

    factorial!(5);

    trace_macros!(false);
}
▶ Run

macro_rules! 调试技巧

rust
macro_rules! debug_macro {
    ($($tt:tt)*) => {{
        // 打印原始 tokens
        println!("输入:{}", stringify!($($tt)*));

        // 继续正常处理
        // ...
    }};
}
▶ Run

完整示例

示例 1:日志宏

rust
macro_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]);
}
▶ Run

示例 2:HashMap 字面量

rust
macro_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);
}
▶ Run

示例 3:SQL 查询构建

rust
macro_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
}
▶ Run

常见错误

错误 1:优先级问题

rust
macro_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)
    };
}
▶ Run

错误 2:变量捕获

rust
macro_rules! bad_macro {
    ($x:expr) => {
        let x = $x;  // ❌ 可能捕获意外的 x
    };
}

// ✅ 使用唯一变量名
macro_rules! good_macro {
    ($x:expr) => {
        let __macro_x = $x;
    };
}
▶ Run

错误 3:片段类型错误

rust
macro_rules! wrong_type {
    ($x:ident) => { /* ... */ };
}

fn main() {
    // ❌ 错误:123 不是 ident
    // wrong_type!(123);

    // ✅ 正确
    wrong_type!(my_var);
}
▶ Run

宏最佳实践

┌─────────────────────────────────────────────────────┐
│              宏最佳实践                              │
├─────────────────────────────────────────────────────┤
│                                                     │
│  1. 优先使用函数                                     │
│     • 函数更容易理解和调试                           │
│     • 只在必要时使用宏                               │
│                                                     │
│  2. 遵循 Rust 语法                                   │
│     • 让宏调用看起来像普通 Rust 代码                  │
│     • 使用熟悉的语法结构                           │
│                                                     │
│  3. 提供良好文档                                     │
│     • 说明宏的用法                                   │
│     • 提供示例                                       │
│                                                     │
│  4. 注意卫生                                         │
│     • 避免变量名冲突                                 │
│     • 使用唯一内部变量名                             │
│                                                     │
│  5. 测试宏                                           │
│     • 测试各种输入情况                               │
│     • 测试边界情况                                   │
│                                                     │
└─────────────────────────────────────────────────────┘

练习

练习 1:Getter/Setter 宏

创建一个宏,为结构体字段生成 getter 和 setter 方法:

rust
generate_accessors! {
    struct Person {
        name: String,
        age: u32,
    }
}
▶ Run

练习 2:断言宏

实现一个增强版断言宏,显示更多调试信息:

rust
assert_with_info!(x > 0, "x 必须是正数", x = x);
▶ Run

练习 3:简化错误处理

创建一个简化错误处理的宏:

rust
let file = try_or_return!(File::open("test.txt"));
▶ Run

小结

本章我们学习了:

  • ✅ macro_rules! 语法
  • ✅ 宏片段类型
  • ✅ 重复匹配
  • ✅ 内置宏
  • ✅ 递归宏
  • ✅ 过程宏基础
  • ✅ 调试宏的技巧

常用内置宏速查

用途
stringify!转 token 为字符串
concat!拼接字符串
env!获取环境变量
include!包含文件
compile_error!编译错误
line! / file!行号/文件名
module_path!模块路径
format_args!格式化参数

第 26 章:命令行工具