宏系统

宏(Macro)在编译时展开代码,提供元编程能力。

声明宏

使用 macro_rules! 定义声明宏:

macro_rules! say_hello {
    () => {
        println!("Hello!");
    };
}

fn main() {
    say_hello!();  // 展开为 println!("Hello!");
}

带参数的宏

macro_rules! create_function {
    ($func_name:ident) => {
        fn $func_name() {
            println!("函数 {:?} 被调用", stringify!($func_name));
        }
    };
}

create_function!(foo);
create_function!(bar);

fn main() {
    foo();  // "函数 "foo" 被调用"
    bar();  // "函数 "bar" 被调用"
}

模式匹配

macro_rules! vec_of_strings {
    ($($x:expr),*) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x.to_string());
            )*
            temp_vec
        }
    };
}

fn main() {
    let v = vec_of_strings!["hello", "world"];
    println!("{:?}", v);  // ["hello", "world"]
}

常用的内置宏

println! 和 format!

fn main() {
    println!("Hello, {}!", "world");
    let s = format!("x = {}, y = {}", 10, 20);
}

vec!

fn main() {
    let v = vec![1, 2, 3];

    // 等同于
    let mut v = Vec::new();
    v.push(1);
    v.push(2);
    v.push(3);
}

panic!

fn main() {
    panic!("发生了致命错误!");
}

assert! 和 debug_assert!

fn main() {
    assert!(1 + 1 == 2, "数学出错了!");
    assert_eq!(1 + 1, 2);
    assert_ne!(1, 2);

    // 只在 debug 模式下检查
    debug_assert!(expensive_check());
}

fn expensive_check() -> bool {
    true
}

派生宏

使用 #[derive] 自动实现 trait:

#[derive(Debug, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p1 = Point { x: 1, y: 2 };
    let p2 = p1.clone();

    println!("{:?}", p1);  // Debug
    assert_eq!(p1, p2);    // PartialEq
}

属性宏

#[test]
fn it_works() {
    assert_eq!(2 + 2, 4);
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_add() {
        assert_eq!(2 + 2, 4);
    }
}

过程宏(高级)

过程宏需要单独的 crate:

// 在 my_macro crate 中
use proc_macro::TokenStream;

#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // 实现代码生成逻辑
    // ...
}

使用:

use my_macro::HelloMacro;

#[derive(HelloMacro)]
struct Pancakes;

fn main() {
    Pancakes::hello_macro();
}

宏调试

macro_rules! debug_macro {
    ($($x:tt)*) => {
        {
            println!("展开为:");
            $($x)*
        }
    };
}

fn main() {
    debug_macro! {
        let x = 42;
        println!("{}", x);
    }
}

使用 cargo expand 查看宏展开结果:

cargo install cargo-expand
cargo expand

宏 vs 函数

特性函数
调用语法name!()name()
展开时机编译时运行时
参数个数可变固定
参数类型任意固定
卫生性无关

小结

  • ✅ 宏在编译时展开代码
  • macro_rules! 定义声明宏
  • ✅ 派生宏自动实现 trait
  • ✅ 过程宏提供高级元编程
  • ✅ 宏比函数更灵活,但更难调试