学习 Rust 声明宏

Rust 的声明宏是一种类似于 C 语言中的宏, 用于声明代码片段, 通过宏展开生成代码.

语法

声明宏使用 macro_rules! 关键字声明, 语法如下:

macro_rules! macro_name {
    (pattern1) => {
        // code1
    };
    (pattern2) => {
        // code2
    };
}

元变量

  • 从 2021 edition 开始, pat 可以匹配顶层或模式
  • 在 2021 edition 之前, patpat_param 相同

重复

和正则类似, 可以使用 *, +, ? 来表示重复次数:

使用

macro_use

装饰模块, 使得模块中的宏可以在其他模块中使用

macro_export

装饰宏, 使宏在 crate:: 域中声明, 即可以使用 crate::my_macro! 的形式调用

示例

多规则

View in Playground

macro_rules! add {
    ($a: expr, $b: expr) => {
        $a + $b
    };
    ($a: expr, $b: expr, $c: expr) => {
        $a + $b + $c
    };
    ($a: expr, $b: expr, $c: expr, $d: expr) => {
        $a + $b + $c + $d
    };
}
 
fn main() {
    let sum1 = add!(1, 2);
    let sum2 = add!(1, 2, 3);
    let sum3 = add!(1, 2, 3, 4);
    println!("sum1: {}, sum2: {}, sum3: {}", sum1, sum2, sum3);
    // sum1: 3, sum2: 6, sum3: 10
}

重复 & 嵌套调用

View in Playground

macro_rules! add {
    ($a: expr) => {
        $a
    };
    ($a: expr, $($b: expr),+) => {
        $a + add!($($b),+)
    };
}
 
fn main() {
    let sum1 = add!(1, 2);
    let sum2 = add!(1, 2, 3);
    let sum3 = add!(1, 2, 3, 4);
    let sum4 = add!(1, 2, 3, 4, 5);
    let sum5 = add!(1, 2, 3, 4, 5, 6);
    println!(
        "sum1: {}, sum2: {}, sum3: {}, sum4: {}, sum5: {}",
        sum1, sum2, sum3, sum4, sum5
    );
    // sum1: 3, sum2: 6, sum3: 10, sum4: 15, sum5: 21
}

综合使用

View in Playground

macro_rules! my_errors {
    ($( $variant: ident => $reason: expr ),* $(,)?) => {
        #[derive(Debug)]
        pub enum MyError {
            $($variant { reason: &'static str },)*
        }
 
        impl MyError {
            $(
            #[allow(non_upper_case_globals)]
            pub const $variant: MyError = MyError::$variant { reason: $reason };
            )*
 
            pub fn reason(&self) -> &str {
                match self {
                    $(MyError::$variant { reason } => reason),*
                }
            }
        }
    }
}
 
my_errors!(
    NotFound => "not found",
    BadRequest => "bad request",
    Unauthorized => "unauthorized",
);
 
fn main() {
    let error1 = MyError::NotFound;
    let error2 = MyError::BadRequest;
    let error3 = MyError::Unauthorized;
 
    println!("{}", error1.reason());
    println!("{}", error2.reason());
    println!("{}", error3.reason());
    // not found
    // bad request
    // unauthorized
}

复杂案例

标记树撕咬机 (tt muncher)

References