「这是我参与2022首次更文挑战的第 18 天,活动详情查看:2022首次更文挑战」。
Rust宏也支持接受非固定数量的参数。这些操作符与正则表达式非常相似:
*→ 用于零个或多个标记类型;+→ 用于零个或一个参数;
macro_rules! add_as{
(
// repeated block
$($a:expr)
// seperator
,
// zero or more
*
) => {
{
// to handle the case without any arguments
0
// block to be repeated
$(+$a)*
}
}
}
fn main() {
println!("{}", add_as!(1, 2, 3, 4)); // => println!("{}",{0+1+2+3+4})
}
- 重复的标记类型用
$()括起来,后面是分隔符和*/+,表示该标记将重复的次数。 - 分隔符是用来区分标记之间的区别。
$()块后面的*/+是用来表示重复的代码块的。在上面的例子,+$a是一个重复的代码。
如果你仔细观察,你会发现在代码中加入了一个额外的零,以其使语法有效。为了去除这个零,使添加的表达式与参数相同,我们需要创建一个新的宏,称为 TT muncher。
macro_rules! add{
// first arm in case of single argument and last remaining variable/number
($a:expr)=>{
$a
};
// second arm in case of two arument are passed and stop recursion in case of odd number ofarguments
($a:expr,$b:expr)=>{
{
$a+$b
}
};
// add the number and the result of remaining arguments
($a:expr,$($b:tt)*)=>{
{
$a+add!($($b)*)
}
}
}
fn main() {
println!("{}", add!(1, 2, 3, 4));
}
TT muncher 以递归的方式分别处理每个标记。一次只处理一个标记是比较容易的。这个宏有三个分支:
- 单参数传递的情况
- 传递两个参数的情况
- 再次调用
add!,并输入其余的参数
宏的参数不需要用逗号隔开。多个标记可以使用不同的标记类型。例如,括号可以与ident标记类型一起使用。Rust编译器会匹配对应分支,从参数字符串中提取变量。
macro_rules! ok_or_return{
// match something(q,r,t,6,7,8) etc
// compiler extracts function name and arguments. It injects the values in respective varibles.
($a:ident($($b:tt)*))=>{
{
match $a($($b)*) {
Ok(value)=>value,
Err(err)=>{
return Err(err);
}
}
}
};
}
fn some_work(i: i64, j: i64) -> Result<(i64, i64), String> {
if i + j > 2 {
Ok((i, j))
} else {
Err("error".to_owned())
}
}
fn main() -> Result<(), String> {
ok_or_return!(some_work(1, 4));
ok_or_return!(some_work(1, 0));
Ok(())
}
ok_or_return macro 将一个函数作为参数,并在匹配语句内执行该函数。对于传递给函数的参数,它采用重复的方式。
通常情况下,少数宏需要被组合成一个宏。在这些情况下,会使用内部宏规则。它有助于操作宏的输入,并写出干净的 TT Munchers。
要创建一个内部规则,请添加以@开头的规则名称作为参数。现在,除非明确指定为参数,否则宏将永远不会匹配内部规则。