rust 为什么没有看到与宏匹配的宏臂?

monwx1rj  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(93)

这是我可以创建的MVP,但它基于一段真实的代码:

fn testmatch() -> i32 {
    let dependant = 4;
    
    macro_rules! testmacro {
        () => { return 0 };
    
        ({ $cond:expr => $res:literal } $(, $rest:tt)*) => {
            if $cond {
                return $res;
            } else {
                return testmacro!($($rest)*);
            }
        };
        
        (unfurl!($furled:ident) $(, $rest:tt)*) => {
            furls!($furled);
            testmacro!($($rest)*);
        };
    }
    
    macro_rules! furls {
        (test_furl) => { testmacro! {
            { dependant == 4 => 4 },
            { dependant == 5 => 5 }
        } };
    }

    match 1 {
        1 => testmacro! {
            { dependant == 2 => 2 },
            { dependant == 3 => 3 },
            unfurl!(test_furl)
        },
        _ => 4000000
    }
}

fn main() {
    let x = testmatch();
    println!("{}", x);
}

我把它放在match语句中,因为这是我的实际代码的结构,但我不相信这会有什么不同。我明白在这个例子中,简单地使用内联if语句会更容易,但是如果需要对每个条件和结果进行重复逻辑,我相信这是最有效的方法。
错误如下:

error: no rules expected the token `!`
  --> src/main.rs:32:19
   |
4  |     macro_rules! testmacro {
   |     ---------------------- when calling this macro
...
32 |             unfurl!(test_furl)
   |                   ^ no rules expected this token in macro call
   |
   = note: while trying to match sequence end

warning: unused macro definition: `furls`
  --> src/main.rs:21:18
   |
21 |     macro_rules! furls {
   |                  ^^^^^
   |
   = note: `#[warn(unused_macros)]` on by default

不仅unfurl!与宏不匹配,而且它似乎也没有使用后来定义的furls!宏。为什么连展开都看不见?如何在这种语法中扩展furl?

mnemlml8

mnemlml81#

正如@kmdreko所解释的,tt匹配一个 token tree -一对{}()[]以及里面的任何东西,或者任何其他单个token。这些条件是匹配的,因为它们包含在{}中;但结果不是,而且它也不是一个单一的标记,所以它会出错。
解决这个问题的最简单的方法就是把它也放在括号里。你还需要纠正另外两个错误:在展开其他条件并将最后一个手臂包含在块中时,将逗号放回原处,因为它有两个语句:

fn testmatch() -> i32 {
    let dependant = 4;
    
    macro_rules! testmacro {
        () => { return 0 };
    
        ({ $cond:expr => $res:literal } $(, $rest:tt)*) => {
            if $cond {
                return $res;
            } else {
                return testmacro!($($rest),*);
            }
        };
        
        ((unfurl!($furled:ident)) $(, $rest:tt)*) => {{
            furls!($furled);
            testmacro!($($rest)*);
        }};
    }
    
    macro_rules! furls {
        (test_furl) => { testmacro! {
            { dependant == 4 => 4 },
            { dependant == 5 => 5 }
        } };
    }

    match 1 {
        1 => testmacro! {
            { dependant == 2 => 2 },
            { dependant == 3 => 3 },
            (unfurl!(test_furl))
        },
        _ => 4000000
    }
}

第二种最简单的方法是将逗号 * 放在前一个条件 * 之后,而不是 * 放在下一个条件 * 之前。如果只有条件(没有结果),则需要在末尾加上逗号。另外,我们需要做的另一件事是省略结果后面的逗号,即使我们在它后面有条件:

fn testmatch() -> i32 {
    let dependant = 4;
    
    macro_rules! testmacro {
        () => { return 0 };
    
        ({ $cond:expr => $res:literal }, $($rest:tt)*) => {
            if $cond {
                return $res;
            } else {
                return testmacro!($($rest)*);
            }
        };
        
        (unfurl!($furled:ident) $($rest:tt)*) => {{
            furls!($furled);
            testmacro!($($rest)*);
        }};
    }
    
    macro_rules! furls {
        (test_furl) => { testmacro! {
            { dependant == 4 => 4 },
            { dependant == 5 => 5 },
        } };
    }

    match 1 {
        1 => testmacro! {
            { dependant == 2 => 2 },
            { dependant == 3 => 3 },
            unfurl!(test_furl)
        },
        _ => 4000000
    }
}

最难的事情是保持语法不变,但我会把它作为一个挑战留给你:)提示:这需要TT munching

相关问题