「深挖Rust」Rust macro 实例 - 2

322 阅读1分钟

「这是我参与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})
}
  1. 重复的标记类型用 $() 括起来,后面是分隔符和 */+,表示该标记将重复的次数。
  2. 分隔符是用来区分标记之间的区别。
  3. $() 块后面的 */+ 是用来表示重复的代码块的。在上面的例子,+$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 以递归的方式分别处理每个标记。一次只处理一个标记是比较容易的。这个宏有三个分支:

  1. 单参数传递的情况
  2. 传递两个参数的情况
  3. 再次调用 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

要创建一个内部规则,请添加以@开头的规则名称作为参数。现在,除非明确指定为参数,否则宏将永远不会匹配内部规则。