无法编译此宏规则:需要类型注释

Can't make this macro rule compile: type annotation needed

在下面的代码中,我真的没有找到一种方法来编译宏规则 args!,而不用像在 main 函数中那样改变它的语法:

use std::rc::Rc;

trait Checker: Fn(i32) -> bool {}
impl<F> Checker for F where F: Fn(i32) -> bool {}

fn is_odd(n: i32) -> bool {
    n % 2 == 1
}

fn is_positive(n: i32) -> bool {
    n >= 0
}

fn and(a: impl Checker, b: impl Checker) -> impl Checker {
    move |n: i32| a(n) && b(n)
}

macro_rules! args {
    ( $($name:ident : $checker:expr),* $(,)? ) => {{
        let args: Vec<(String, Option<Rc<dyn Checker>>)> = vec![
            $( args!(__arg stringify!($name).to_string(), $checker) ),*
        ];
        args
    }};
    // LINE 26: the following rule is never matched:
    (__arg $name:expr, None) => {
        ($name, None)
    };
    (__arg $name:expr, $checker:expr) => {
        (
            $name,
            // LINE 33: can't give a type for argument `c` (this is a fn type):
            $checker.map(|c| {
                let c: Rc<dyn Checker> = Rc::new(c);
                c
            })
        )
    };
}

fn main() {
    let _args = args![
        a: Some(is_odd),
        b: Some(and(is_positive, is_odd)),
        c: None, // <-- This compiles well if this line is commented
    ];
}

这给出了这个错误:

error[E0282]: type annotations needed
  --> src/main.rs:33:27
   |
33 |               $checker.map(|c| {
   |                             ^ consider giving this closure parameter a type
...
42 |       let _args = args![
   |  _________________-
43 | |         a: Some(is_odd),
44 | |         b: Some(and(is_positive, is_odd)),
45 | |         c: None, // <-- This compiles well if this line is commented
46 | |     ];
   | |_____- in this macro invocation

问题是 None.map(|c| ...)(第 33 行)需要 c 的类型;但我不能给一个,因为它无法命名,而且每次都会改变。

所以我尝试在宏中添加一个规则,专门用于 None(第 26 行);但规则从不匹配:我猜这是因为在宏的第一个主要规则中,$checker 匹配为 expr,但模式 None 肯定被视为令牌树.

我发现的唯一解决方法是通过在每个 arg 定义周围添加方括号并像这样更改宏的主要规则来使最终语法更加繁琐(但我想避免它):

( $( [ $name:ident : $($checker:tt)* ] ),* $(,)? ) => ...

如何更正此宏规则以使其生效?

不幸的是,为了让 (__arg $name:expr, None) 规则匹配,您必须匹配您的 $checkers tts 是唯一可行的在这种情况下)。正如您自己发现的那样,这有时会很麻烦。

更简单的(至少对用户而言)宏语法怎么样?

macro_rules! args {
    ( $($name:ident $(: $checker:expr)?),* $(,)? ) => {{
        let args: Vec<(String, Option<Rc<dyn Checker>>)> = vec![
            $( args!(__arg stringify!($name).to_string() $(, $checker)?) ),*
        ];
        args
    }};
    (__arg $name:expr) => {
        ($name, None)
    };
    (__arg $name:expr, $checker:expr) => {
        (
            $name,
            {
                let c: Rc<dyn Checker> = Rc::new($checker);
                Some(c)
            }
        )
    };
}

在您的示例中可以将其称为

let _args = args![
    a: is_odd,
    b: and(is_positive, is_odd),
    c,
];

我知道这不会直接修复您的宏,但是如果您在宏参数中使用 Option 的唯一原因是指定是否存在 Checker,这可能是一个很好的(并且可以说是更清洁的)替代品。

Playground