我正在尝试构建一个宏,该宏将使用函数调用列表 ( test2() ) 或标识符 ( test3 ) 进行调用:

invoke!(
    ["str1", "str2"]: i32 => test1(), test2(), test3, test4;
    ["str3", "str1"]: i32 => test1(), test3;
)

目前,它只支持函数调用:
invoke!(
    ["str1", "str2"]: i32 => test1(), test2();
    ["str3", "str1"]: i32 => test1();
)

原始宏代码:
macro_rules! invoke {
    ( $([$($field:expr),*]: $vtype:ty => $($func:ident$args:tt),*; )+ ) => {
        $(
            let x = vec![$($field),*];
            let y = vec![$($func$args),*];
            println!("{:#?}: {:#?}", x, y)
        )+
    };
}

当我尝试将其更改为支持两种形式时:
macro_rules! invoke {
    ( $([$($field:expr),*]: $vtype:ty => $($func:ident$args:tt),* $($newfunc:ident),*; )+ ) => {
        $(
            let x = vec![$($field),*];
            let y = vec![$($func$args),*];
            let z: Vec<$vtype> = vec![$newfunc()];
            println!("{:#?}: {:#?} [{:#?}]", x, y, z)
        )+
    };
}

我收到一个错误:

error: local ambiguity: multiple parsing options: built-in NTs ident ('func') or ident ('newfunc').
  --> src/main.rs:15:34
   |
15 |         ["str1", "str2"]: i32 => test1(), test2(), test3, test4;
   |                                  ^^^^^

我知道可以通过 :tt 构建这个宏,但我找不到方法。

Working code sample

Expected and not working code sample

最佳答案

这个宏模式没有意义:

$($func:ident$args:tt),* $($newfunc:ident),*;

这表示要解析零个或多个标识符 ( func ) 后跟零个或多个标识符 ( newfunc )。如果输入是 ident foo ,那么解析器将无法决定选择哪个语法路径,因为前瞻有限(大概是一个标记)。

正如评论中所指出的,最简单的解决方案是引入新的语法来消除这些情况的歧义。
macro_rules! invoke {
    ( $([$($field:expr),*]: $vtype:ty => $($func:ident$args:tt),* [$($newfunc:ident),*]; )+ ) => {{
        $(
            let x = vec![$($field),*];
            let y = vec![$($func$args),*];
            let z: Vec<$vtype> = vec![$($newfunc()),*];
            println!("{:#?}: {:#?} [{:#?}]", x, y, z);
        )+
    }};
}

fn main() {
    invoke!(
        ["str1", "str2"]: i32 => test1(), test2() [test3, test4];
        ["str3", "str1"]: i32 => test1() [test3];
    );
}

fn test1() -> i32 { 42 }
fn test2() -> i32 { 42 }
fn test3() -> i32 { 42 }
fn test4() -> i32 { 42 }



我所知道的宏中没有可选的修饰符。通常,这是通过具有多个并行模式来处理的:
macro_rules! thing {
    ($name:ident$args:tt) => { println!("a") };
    ($name:ident) => { println!("b") };
}

fn main() {
    thing!(foo);   // b
    thing!(foo()); // a
}

这似乎对您的情况没有帮助,因为您希望先收集一堆 A,然后再收集一堆 B。这将允许一堆 A 或 B。在语法上,它类似于

(A|B)* # This form
A*B*   # What you want

关于macros - 向 "local ambiguity: multiple parsing options"中的多次重复结果添加 ident,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42885668/

10-11 13:21