Reputation: 21767
I have some repetitive code
match *x {
A(ref a) => "special",
B(ref a) => "B foo",
C(ref a) => "C foo",
D(ref a) => "D foo",
// ...
}
I would like a macro like
macro_rules! generic_fmt {
($T:ident) => {
$T(ref a) => {"$T foo"},
}
}
So that I can simplify my matching to
match *x {
A(ref a) => "special",
generic_fmt!(B),
generic_fmt!(C),
generic_fmt!(D),
// ...
}
What's the best way to do that? I'm using rustc 1.19.0-nightly.
Upvotes: 10
Views: 5058
Reputation: 30101
You cannot do that exactly. A macro cannot expand to a match
arm (<Variant> => <Expression>
).
The closest you could get is probably something like this:
enum Foo {
A(u32),
B(u32),
C(u32),
D(u32),
}
macro_rules! gen1 {
($Variant:ident) => {
Foo::$Variant(ref a)
}
}
macro_rules! gen2 {
($Variant:ident) => {
concat!(stringify!($Variant), " foo")
}
}
fn print(x: Foo) {
println!("{}", match x {
Foo::A(ref a) => "special",
gen1!(B) => gen2!(B),
gen1!(C) => gen2!(C),
gen1!(D) => gen2!(D),
});
}
fn main() {
print(Foo::A(42));
print(Foo::B(42));
print(Foo::C(42));
print(Foo::D(42));
}
Upvotes: 11
Reputation: 733
If changing your output a little is acceptable, you could use a catch-all for all the same arms:
match x {
Foo::A(ref a) => println!("special"),
_ => println!("{:?} Foo", x),
}
But this would print the type and its parameters. If you are on nightly, and not afraid of experimental, you can use std::intrinsics::type_name
to display only the type name.
Or you can use a macro that does all your match arms:
macro_rules! gen_match {
($x:ident, $Special:ident, [$( $Foo:ident ),*]) => {
match $x {
Foo::$Special(ref a) => println!("special"),
$(Foo::$Foo(ref a) => println!("{} foo", stringify!($Foo)),)*
}
}
}
and calling it:
gen_match!(x, A, [B, C, D]);
The brackets around the variants that will be generic formatted are for readability, they could be removed from macro definition
Upvotes: 5