Macro nar_dev_utils::macro_once

source ·
macro_rules! macro_once {
    (
        $(#[$attr:meta])* // ✨允许文档注释
        macro $name:ident ( $($pattern:tt)* ) {
            $($body:tt)*
        }
        $($usage:tt)*
    ) => { ... };
    (
        $(#[$attr:meta])* // ✨允许文档注释
        macro $name:ident {
            $(
                ( $($pattern:tt)* ) => { $($body:tt)* } $(;)?
            )*
        }
        $($usage:tt)*
    ) => { ... };
    (
        $(#[$attr:meta])* // ✨允许文档注释,但实际上用处不大
        macro ( $($pattern:tt)* ) => {
            $($body:tt)*
        }
        $($usage:tt)*
    ) => { ... };
    (
        $(#[$attr:meta])*
        macro => { // * 🚩【2024-05-02 16:56:57】为统一形式,还是加上箭头好
            $(
                ( $($pattern:tt)* ) => { $($body:tt)* } $(;)?
            )*
        }
        $($usage:tt)*
    ) => { ... };
}
Expand description

§立即宏

  • 🎯在一些非常专用的地方节省代码
  • 🎯在定义宏但其不能通用的情况节省认知负担
  • 🚩定义一次宏,然后立即 使用/调用/消耗 它
    • ✨对「一次性表达式」可匿名
    • ✨若为「条目宏」,其后仍然可以导出
  • 📌对匿名宏的卫生性保证
    • ✅基于macro_rules的卫生性,使用该宏定义的「一次性匿名宏」不会占用已有标识符
      • 🚩对于匿名宏,要调用自身,可使用标识符_self(硬编码)
    • ✅基于macro_rules的可见性,使用该宏定义的「一次性匿名宏」不会泄漏到其它模块
      • 💭亦可选择性泄漏:属性宏#[macro_export]

§用法

§作为 语句/条目(函数定义、多行代码…)

use nar_dev_utils::macro_once;
macro_once! {
    macro 一次性宏名称( /* 「一次性宏」的匹配模式 */ ) {
        /* 「一次性宏」的目标代码 */
    }
    /* 传入「一次性宏」的代码 */
}

§作为 表达式

use nar_dev_utils::macro_once;
macro_once! {
    macro ( /* 「一次性匿名宏」的匹配模式 */ ) => {
        /* 「一次性匿名宏」的目标代码 */
    }
    /* 传入「一次性匿名宏」的代码 */
}

§测试用例

use nar_dev_utils::macro_once;
use nar_dev_utils::asserts;

// 简单示例 / 表达式 //
const ONE: usize = macro_once! {
    macro () => { 1 }
};
assert_eq!(ONE, 1);

// 简单示例 / 语句
let mut x = 0_i32;
macro_once! {
    macro plus_one($var:ident) {
        $var += 1;
    }
    x
}
assert_eq!(x, 1);

// 简单示例 / 条目 //
macro_once! {
    macro plus(
        $arg_ty:ty;
        $( $name:ident => [$($arg:ident)*]; )*
    ) {
        $( fn $name($($arg: $arg_ty),*) -> $arg_ty { 0 $(+ $arg)* } )*
    }
    i32;
    plus_0 => [];
    plus_1 => [a];
    plus_2 => [a b];
    plus_3 => [a b c];
}
assert_eq!(plus_0(), 0);
assert_eq!(plus_1(1), 1);
assert_eq!(plus_2(1, 2), 3);
assert_eq!(plus_3(1, 2, -3), 0);

// 定义`macro_once`所说的「匿名宏」
macro_rules! macro_anonymous {
    ($a:expr) => {
        $a + 2
    };
}
// 定义并立即使用宏 / 作为表达式
assert_eq!(
    macro_once! {
        macro ($a:expr) => {$a + 1}
        1 + 1
    },
    3
);
// 定义并立即使用宏 / 作为语句
macro_once! {
    macro asserts_plus($( $($num:expr)* => $sum:expr; )*) {
        $( assert_eq!(0 $(+ $num)*, $sum); )*
    }
    1 2 => 3;
    1 3 => 4;
    2 2 => 4;
    2 3 => 5;
}
// 对当前作用域的宏名无影响
assert_eq!(
    macro_anonymous! {
        2
    },
    4
);

// 测试「多分派」 //
macro_once! {
    /// 试验/多分派
    #[macro_export]
    macro multi_dispatch {
        // 入口
        ( $( $name:ident => $arg:tt; )* ) => {
            $(
                multi_dispatch! {
                    $name = $arg
                }
            )*
        }
        // 统一的「赋值」分派
        ( let $name:ident $value:expr ) => {
            let $name = $value;
        }
        // 特殊值分派
        ( $name:ident = 1 ) => {multi_dispatch! {let $name 1} }
        ( $name:ident = 2 ) => {multi_dispatch! {let $name 2} }
        ( $name:ident = 3 ) => {multi_dispatch! {let $name 3} }
        // 口袋分派:默认清零
        ( $name:ident = $unknown:expr ) => {multi_dispatch! {let $name 0} }
    }
    a => 1;
    b => 2;
    c => 3;
    d => 4;
}
// 验证「多分派」是否成立
assert_eq!(a + b + c, 6);
assert_eq!(d, 0);

// 多分派/表达式 //
let tuple = macro_once! {
    macro => {
        // 入口:构造元组
        ( $( { $($value_ex:tt)* } $(,)? )* ) => { ( $( _self! { @ $($value_ex)* } ),* ) }
        // 多分派计算:空元组
        (@ ) => { () }
        // 多分派计算:加法
        (@ $a:literal plus $b:literal ) => { $a + $b }
        // 多分派计算:乘法
        (@ $a:literal times $b:literal ) => { $a + $b }
        // 多分派计算:字符串
        (@ stringify! $($token:tt)* ) => { stringify!($($token)*) }
        // 多分派计算:任意Rust表达式
        (@ rust $e:expr ) => { $e }
    }
    // 立即调用
    {1_i32 plus 2_i32},
    {},
    {2_i32 times 2_i32},
    {stringify! <{SELF} --> [good]>.}
    {rust {
        let mut s = String::new();
        s += "123";
        assert_eq!(s, "123");
        s
    }}
};
// 验证多分派的类型(若不对则编译不通过)
let _: &(i32, (), i32, &str, String) = &tuple;
asserts! {
    // 逐一验证多分派的值 | ⚠️不对空元组直接判等
    tuple.0 == 3
    tuple.2 == 4
    tuple.3 == "<{ SELF } --> [good]>."
    tuple.4 == "123"
}