1

I find myself often defining enum types like the following:

struct Foo { }
struct Bar { }
struct Baz { }

enum MyEnum {
    Foo(Foo),
    Bar(Bar),
    Baz(Baz),
}

I find the repetition of Foo(Foo) inelegant and redundant. It also makes initialization redundant:

let example = MyEnum::Foo(Foo{ /* ... */ });

I would like to instead write something like the following pseudocode:

struct Foo { }
struct Bar { }
struct Baz { }

type_enum MyEnum {
    Foo,
    Bar,
    Baz,
}

let example = MyEnum::Foo{ /* ...anything that `Foo` supports ... */ };

The above is very similar to std::variant in C++17. Does Rust support anything like that?

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • So what wrong with https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6c0e05fc4a108965e096ae37967c24d2 ? – Stargateur Nov 27 '19 at 16:06
  • @VittorioRomeo I think there is one, but unfortunately I do not remember. Still, it shouldn't be too much code, so maybe you could just implement it yourself in your project. – phimuemue Nov 27 '19 at 16:08
  • Note that there is a major difference between `enum`s and `std::variant`: the ability to have different variants of the same contained type, which is very important for some uses of `enum`. I don't know how you could preserve that ability while also eliminating the perceived repetition here, which is probably why no related RFC has really gotten off the ground. I can also imagine interesting (=surprising) interactions with "anonymous `enum`s", which is another commonly asked for feature. – trent Nov 27 '19 at 16:50

2 Answers2

5

Maybe you can use this macro-based solution:

macro_rules! type_dispatch_enum{(enum $e: ident {$($v: ident,)*}) => {
    enum $e {
        $($v($v),)*
    }
    $(
        impl From<$v> for $e {
            fn from(v: $v) -> Self {
                $e::$v(v)
            }
        }
    )+
}}

struct S1 {}
struct S2;
struct S3(usize);

type_dispatch_enum!(enum E {
    S1,
    S2,
    S3,
});

fn take_e(e: E) {}

fn main() {
    take_e(S1{}.into());
    take_e(S2.into());
    take_e(S3(5).into());
}
phimuemue
  • 34,669
  • 9
  • 84
  • 115
3

An enum variant can be struct, tuple or unit struct. So you can simply define:

enum MyEnum {
    Foo {},
    Bar {},
    Baz {},
}

Or if you would like to define these variants as independent structs themselves, I'd argue that a more idiomatic 'functional' way is to use something like Either:

enum Either<L, R> {
    Left(L),
    Right(R),
}

struct Foo {}
struct Bar {}

type MyEnum = Either<Foo, Bar>;

There's a crate with the same name, but this basic definition should be sufficient most of the time.

edwardw
  • 12,652
  • 3
  • 40
  • 51
  • I still want to use `Foo` and `Bar` independently of `MyEnum`. I don't see how the `Either` solution helps, as it still needs names for `L`and `R` (i.e. `Left` and `Right). – Vittorio Romeo Nov 27 '19 at 16:41
  • 2
    @VittorioRomeo nothing stops you from using Rust like C or C++. It is just that with more powerful language mechanisms like pattern matching, your concern here is mostly void. I happen to believe that it is more important to spell out the fact you are using a sum type. – edwardw Nov 27 '19 at 16:48