Concisely put into Rust code, I'm trying to generate a pattern match like this:
if let Foo::Variant(_) = value {}
// ^^^^^^^^^^^^^^^
in a macro, with both Foo
(a type) and Variant
(an identifier) passed to the macro as metavariables. In the real use case, I'm generating a match
instead of an if let
and am using multiple variants of the same enum, but if let
resulted in a shorter reproducible example.
This works with simple enums:
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);
}
This compiles.
However, when I make the enum variant tuple-like, it breaks (changes highlighted):
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
I have tried some variations, more or less blindly; $enum::$variant(_)
, <$enum::$variant>(_)
, <$enum::$variant>::(_)
among them.
Is this possible? Am I perhaps using the wrong types of metavariables?
This question seems to be related, but it focuses on mixing unit and tuple variants, and has not been resolved.
The problem seems to be caused by the $enum
metavariable, as the following slight modifications show:
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 {}
};
}
As the problem happens on the syntax level, we can try change the syntactic makeup of the generated code, in particular by introducing a type alias. We then need to scope that type to not leak out of the macro:
macro_rules! match_enum {
(
$value:ident: <$enum:ty>::$variant:ident
) => {
{
type Enum = $enum;
if let Enum::Variant(_) = $value {}
}
};
}
It's just a workaround, but it's clean enough.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.