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

Matching tuple-like enum variants in a macro, where enum type and variants are metavariables: how do I write the matching pattern?

简明地放入 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 {}
        }
    };
}

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