9 releases
0.3.3 | Oct 12, 2023 |
---|---|
0.3.2 | Aug 5, 2023 |
0.3.1 | Jul 29, 2023 |
0.3.0 | Jun 22, 2023 |
0.1.1 | Mar 19, 2023 |
#540 in Rust patterns
37 downloads per month
13KB
82 lines
nade
English | 简体中文
nade
is a attribute macro that adds named and default arguments to Rust functions.
Usage
// 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)
How it works
If you write a function like this:
// 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
}
it will be expanded to:
// 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)
)
};
}
Then, when you call the macro foo
like this:
use some_crate::{foo, one};
foo!(32, d = 1, c = 2);
it will be expanded to:
use some_crate::{foo, one};
foo(32, one(), 2, 1);
Note
As you can see in How it works, there are 3 things to be aware of in the code generated by #[nade]
.
-
⓵, ⓷
nade_helper
is a declarative macro used to generate function call expressions based on arguments, parameters, and function path.Its path defaults is
$crate::nade_helper
, so you need to import the macro in the root of crate usingpub use nade::base::*;
orpub use nade::base::nade_helper;
.Also you can customize the path of
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
is an attribute macro that makes the visibility of the declarative macro the same as the function. see macro-v for details.Its path defaults is
::nade::__internal::macro_v
.Also you can customize the path of
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 }
Limitations
-
When you call the macro
foo
, you must use theuse
statement to bring the macro into scope.// 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);
Because the attribute macro
#[nade]
will generate a macro with the same name as the function, and the macro use the function in an unhygienic way, so you must use theuse
statement to bring the macro and the function into scope. -
The default argument expression must be imported into the scope of the macro call.
// Good use some_crate::{foo, one}; foo!(32, d = 1, c = 2); // Bad use some_crate::foo; foo!(32, d = 1, c = 2);
Because the default argument expression is evaluated after the
foo
macro is expanded, so it must be imported into the scope of the macro call.
How to bypass the limitations
-
You can pass a module path starting with
$crate
for the#[nade]
attribute macro on the function.#[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 }
it will be expanded to:
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 ) }; }
Then, you can not use the
use
statement to bring the macro and the function into scope, like this:use some_crate::one; some_crate::foo!(32, d = 1, c = 2);
-
In the
#[nade]
attribute macro on the parameter, you can specify the default argument expression using the full path, either$crate::a::expr
, or::a::b::expr
. In fact, when you use#[nade]
on an parameter, you are using#[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); }
it will be expanded to:
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) ) }; }
Then, you can not use the
use
statement to bring default argument expressions into scope, like this:use some_crate::foo; foo!();
Credits
This crate is inspired by these crates:
Dependencies
~0.5–1MB
~22K SLoC