colinfang
colinfang

Reputation: 21767

How to write macro for similar matching arms?

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

Answers (2)

mcarton
mcarton

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));
}

Playground link.

Upvotes: 11

Francois Mockers
Francois Mockers

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),
}

Playground link

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]);

Playground link

The brackets around the variants that will be generic formatted are for readability, they could be removed from macro definition

Upvotes: 5

Related Questions