9 个版本

0.3.3 2023 年 10 月 12 日
0.3.2 2023 年 8 月 5 日
0.3.1 2023 年 7 月 29 日
0.3.0 2023 年 6 月 22 日
0.1.1 2023 年 3 月 19 日

#427Rust 模式

每月 37 次下载

MIT/Apache

13KB
82

纳德

Crates.io version docs.rs docs

英文 | 简体中文

nade 是一个属性宏,用于为 Rust 函数添加命名和默认参数。

用法

// some_crate/src/lib.rs
pub use nade::base::*;
use nade::nade;

pub fn one() -> u32 {
    1
}

#[nade]
pub fn foo(
    /// You can add doc comments to the parameter. It will be shown in the doc of the macro.
    ///
    /// The world is 42.
    #[nade(42)] a: u32,

    /// Call a function
    #[nade(one())] b: u32,

    /// Default value of u32
    #[nade] c: u32,

    d: u32
) -> u32 {
    a + b + c + d
}

assert_eq!(foo!(1, 2, 3, 4), 10);         // foo(1,  2,     3,                  4)
assert_eq!(foo!(d = 2), 45);              // foo(42, one(), Default::default(), 2)
assert_eq!(foo!(1, c = 2, b = 3, 4), 10); // foo(1,  3,     2,                  4)

工作原理

如果你编写了一个像这样的函数

// some_crate/src/lib.rs
pub use nade::base::*;
use nade::nade;

pub fn one() -> u32 {
    1
}

#[nade]
pub fn foo(
    #[nade(42)]
    a: u32,

    #[nade(one())]
    b: u32,

    #[nade]
    c: u32,

    d: u32
) -> u32 {
    a + b + c + d
}

它将被扩展为

// some_crate/src/lib.rs
//
pub use nade::base::*;
use nade::nade;

pub fn one() -> u32 {
    1
}

pub fn foo(a: u32, b: u32, c: u32, d: u32) -> u32 {
    a + b + c + d
}

//
#[::nade::__internal::macro_v(pub)]
macro_rules! foo {
    ($($arguments:tt)*) => {
        //
        $crate::nade_helper!(
            ($($arguments)*)
            (a: u32 = 42, b: u32 = one(), c: u32 = Default::default(), d: u32)
            (foo)
        )
    };
}

然后,当你像这样调用宏 foo

use some_crate::{foo, one};

foo!(32, d = 1, c = 2);

它将被扩展为

use some_crate::{foo, one};

foo(32, one(), 2, 1);

注意

工作原理中所示,在 #[nade] 生成的代码中,有 3 件事情需要注意。

  • ⓵, ⓷

    nade_helper 是一个声明式宏,用于基于参数、参数和函数路径生成函数调用表达式。

    它的路径默认为 $crate::nade_helper,因此您需要使用 pub use nade::base::*;pub use nade::base::nade_helper; 在 crate 的根目录中导入宏。

    您还可以自定义 nade_helper 的路径。

    use nade::nade;
    
    mod custom_nade_helper {
        pub use nade::base::nade_helper;
    }
    
    #[nade]
    #[nade_path(nade_helper = custom_nade_helper)]
    fn custom_nade_helper_path(a: usize) -> usize {
        a
    }
    
  • macro_v 是一个属性宏,使声明式宏的可见性与函数相同。有关详细信息,请参阅 macro-v

    它的路径默认为 ::nade::__internal::macro_v

    您还可以自定义 macro_v 的路径。

    use nade::nade;
    
    mod custom_macro_v {
        pub use nade::__internal::macro_v;
    }
    
    #[nade]
    #[nade_path(macro_v = custom_macro_v)]
    fn custom_macro_v_path(a: usize) -> usize {
        a
    }
    

限制

  1. 当你调用宏 foo 时,你必须使用 use 语句将宏引入作用域。

    // Good
    use some_crate::{foo, one};
    foo!(32, d = 1, c = 2);
    
    // Bad
    use some_crate::one;
    some_crate::foo!(32, d = 1, c = 2);
    

    因为属性宏 #[nade] 将生成与函数同名的新宏,并且该宏以不卫生的方式使用该函数,所以你必须使用 use 语句来将宏和函数引入作用域。

  2. 默认参数表达式必须导入到宏调用的作用域中。

    // Good
    use some_crate::{foo, one};
    foo!(32, d = 1, c = 2);
    
    // Bad
    use some_crate::foo;
    foo!(32, d = 1, c = 2);
    

    因为默认参数表达式是在 #[nade] 宏展开之后评估的,所以它必须导入到宏调用的作用域中。

如何绕过限制

  1. 你可以为函数上的 #[nade] 属性宏传递以 $crate 开头的模块路径。

    #[nade(module_path = $crate::module)] // <--- here
    pub fn foo(
        #[nade(42)]
        a: u32,
    
        #[nade(one())]
        b: u32,
    
        #[nade]
        c: u32,
    
        d: u32
    ) -> u32 {
        a + b + c + d
    }
    

    它将被扩展为

    pub fn foo(a: u32, b: u32, c: u32, d: u32) -> u32 {
        a + b + c + d
    }
    
    #[::nade::__internal::macro_v(pub)]
    macro_rules! foo {
        ($($arguments:tt)*) => {
            $crate::nade_helper!(
                ($($arguments)*)
                (a: u32 = 42, b: u32 = one(), c: u32 = Default::default(), d: u32)
                ($crate::module::foo) // <--- here
            )
        };
    }
    

    然后,你不需要使用 use 语句来将宏和函数引入作用域,如下所示

    use some_crate::one;
    some_crate::foo!(32, d = 1, c = 2);
    
  2. 在参数的 #[nade] 属性宏中,你可以使用完整路径指定默认参数表达式,无论是 $crate::a::expr,还是 ::a::b::expr。实际上,当你在参数上使用 #[nade] 时,你实际上是在使用 #[nade(::core::default::Default::default())].

    pub fn one() -> u32 {
        1
    }
    
    pub static PATH: &str = "a";
    
    #[nade]
    pub fn foo<T1, T2, T3, T4>(
        #[nade($crate::module::one())]
        a: T1,
    
        #[nade(::std::path::Path::new("a"))]
        b: T2,
    
        #[nade($crate::module::PATH)]
        c: T3,
    
        #[nade("Hello")]
        d: T4
    ) {
        let _ = (a, b, c, d);
    }
    

    它将被扩展为

    pub fn foo<T1, T2, T3, T4>(a: T1, b: T2, c: T3, d: T4) {
        let _ = (a, b, c, d);
    }
    
    #[::nade::__internal::macro_v(pub)]
    macro_rules! foo {
        ($($arguments:tt)*) => {
            $crate::nade_helper!(
                ($($arguments)*)
                (
                    a: T1 = $crate::module::one(),
                    b: T2 = ::std::path::Path::new("a"),
                    c: T3 = $crate::module::PATH,
                    d: T4 = "Hello",
                )
                (foo)
            )
        };
    }
    

    然后,你不需要使用 use 语句来将默认参数表达式引入作用域,如下所示

    use some_crate::foo;
    foo!();
    

致谢

这个 crate 受以下 crate 启发

依赖

~0.5–1MB
~24K SLoC