How to dispatch enum function to all variants

Situation:

  • An enum with over 50 variants.
  • Each variant implements its own variation of the show function
  • An array of enum is created
  • Enum implements a show function that dispatches (statically) to the variants show function

Is there a way to avoid having to type the match with the 50 variants?

playground

You could implement Display on your VariantA, etc, and then use derive_more::Display to implement it for the enum.

Having Display gets you ToString, and to_string() has the same signature as your show. You could then rewrite your shows to be something like

    fn show(&self) -> String {
        let s = self.to_string();
        println!("show: {s}");
        s
    }

And you could even make this your own trait so you only write it once:

pub trait Show: ToString {
    fn show(&self) -> String { /* as above */ }
}

impl<T: ToString + ?Sized> Show for T {}

Playground, except derive_more isn't available there.

3 Likes

If you don't want to be constrained by the traits derive_more forwards, this would be an ideal problem to solve with your own derive macro. To learn about proc macros I recommend the proc macro workshop by dtolnay.
If you don't mind a little boilerplate you could also use a declarative macro like described here: rust - Calling common method on enum types - Stack Overflow

Btw: it should be relatively straight forward to solve this for the general case of forwarding some function call to all enum variants. You could for example have an attribute like
#[forward(show(&self))]
Does anyone know whether this already exists?

2 Likes

For instance:

macro_rules! impl_various {
    ($($var:ident),*) => {
        enum Various { $(
            $var($var),
        )*}

        impl Various {
            fn show(&self) -> String {
                match &self {
                    $(Various::$var(variant) => variant.show(),)*
                }
            }
        }
    }
}

impl_various!{ VariantA, VariantB }
2 Likes

ambassador can I think, but I haven't played with it.

1 Like

I've written one such crate: https://crates.io/crates/impl-enum

#[impl_enum::with_methods {
    fn show(&self) -> String
}]
enum Various {
    VariantA(VariantA),
    VariantB(VariantB),
    // ....
}

It's similar to ambassador's delegatable_trait_remote but doesn't involve traits which could be a good or bad thing depending on what you're doing.

1 Like

Thanks to all for the suggestions.
I'll need some time to process all of it :smiley: