在宏中匹配类似元组的枚举变量,其中枚举类型和变量是元变量:如何编写匹配模式?

问题描述

简而言之,将其放入Rust代码中,我试图生成这样的模式匹配:

if let Foo::Variant(_) = value {}
//     ^^^^^^^^^^^^^^^

在宏中,Foo(一种类型)和Variant(一种标识符)都作为元变量传递给宏。在实际用例中,我生成的是match而不是if let,并且使用相同枚举的多个变体,但是if let导致了一个较短的可重现示例。

这适用于简单的枚举:

enum Foo {
    Variant,}

macro_rules! match_enum {
    (
        $value:ident: <$enum:ty>::$variant:ident
    ) => {
        if let <$enum>::$variant = $value {}
    };
}

fn main() {
    let foo = Foo::Variant;
    match_enum!(foo: <Foo>::Variant);
}

这会编译。

但是,当我使枚举变量类似于元组时,它会中断(更改以高亮显示):

enum Foo {
    Variant(usize),//         ^^^^^^^
}

macro_rules! match_enum {
    (
        $value:ident: <$enum:ty>::$variant:ident
    ) => {
        if let <$enum>::$variant(_) = $value {}
//                              ^^^
    };
}

fn main() {
    let foo = Foo::Variant(0);
//                        ^^^
    match_enum!(foo: <Foo>::Variant);
}
   |         if let <$enum>::$variant(_) = $value {}
   |                -----------------^^^ unexpected `(` after qualified path
   |                |
   |                the qualified path
...
   |     match_enum!(foo: <Foo>::Variant);
   |     --------------------------------- in this macro invocation

我或多或少盲目地尝试了一些变化;其中有$enum::$variant(_)<$enum::$variant>(_)<$enum::$variant>::(_)

这可能吗?我可能使用了错误类型的元变量吗?

This question似乎是相关的,但它侧重于混合单元和元组变体,尚未解决

解决方法

问题似乎是由$enum元变量引起的,如以下所示:

macro_rules! match_enum {
    (
        $value:ident: <$enum:ty>::$variant:ident
    ) => {
        // does not fix the problem
        if let <$enum>::Variant(_) = $value {}
        // fixes the problem
        if let Bar::$variant(_) = $value {}
    };
}

当问题发生在语法级别时,我们可以尝试更改所生成代码的语法结构,特别是通过引入类型别名。然后,我们需要确定该类型的范围,以免泄漏出宏:

macro_rules! match_enum {
    (
        $value:ident: <$enum:ty>::$variant:ident
    ) => {
        {
            type Enum = $enum;
            if let Enum::Variant(_) = $value {}
        }
    };
}

这只是一种解决方法,但是足够干净。