37

If I have an enum with a set of values, is there a way I could create a second enum with the same variants plus some more?

// From this
enum Base {
    Alpha,
    Beta(usize),
}

// To this, but without copy & paste
enum Extended {
    Alpha,
    Beta(usize),
    Gamma,
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • A more complex form of this Question is [here](https://stackoverflow.com/questions/69218879/rust-extend-enum-result?noredirect=1#comment122342399_69218879). – JamesThomasMoon Sep 17 '21 at 09:15

2 Answers2

43

An enum can't be directly extended, but you use the same composition trick one would use with structs (that is, with a struct, one would have a field storing an instance of the 'parent').

enum Base {
    Alpha,
    Beta(usize),
}

enum Extended {
    Base(Base),
    Gamma
}

If you wish to handle each case individually, this is then used like

match some_extended {
    Base(Alpha) => ...,
    Base(Beta(x)) => ...,
    Gamma => ...
}

but you can also share/re-use code from the "parent"

match some_extended {
    Base(base) => base.do_something(),
    Gamma => ...,
}
JamesThomasMoon
  • 6,169
  • 7
  • 37
  • 63
huon
  • 94,605
  • 21
  • 231
  • 225
  • 11
    Interesting. But what if the "parental chain" gets longer, so the navigation `Base(Base(Base(Alpha)))` or `base.base.base.do_something()` gets endless? – iago-lito Feb 07 '18 at 12:57
  • 4
    I checked and was surprised to find that **the compiler will merge the discriminators**. So in this example `enum Parent{A,B}enum Child{Parent(Parent),C,D}`, the `Child` enum occupies one byte. – Indiana Kernick Dec 18 '20 at 06:07
  • Seems like one case where inheritance is useful and got wrongly banned by some manichean composition hipsters – Alexandre Daubricourt Jun 08 '23 at 06:41
0

You can't, but you can require that a generic type is a superset of an enum T.

There are strictly limited circumstances where this is useful, but it can work around the 'cant extend enums' problem specifically with regard to events.

In the specific subset of cases where you have a library that is generic over an event type TEvent but the library has internal events as well, you often encounter a situation where the library will take an argument of a wrapper event type, like:

enum TLibEvent<T> {
  CustomEvent(T),
  InternalEvent(MyInternalEventType)
}

As described in the accepted solution. However, you don't need to do this.

You can use a single flat top level event type by requiring From<MyInternalEventType> on the generic event type to the library.

Example: (look at the very bottom to see how this works in practice)

enum Events {
    A,
    B
}

#[derive(Debug)]
struct Foo<T> {
    pub events: Vec<T>
}

impl<T: From<Events> + PartialEq> Foo<T> 
{
    pub fn new() -> Foo<T> {
        Foo {
            events: Vec::new()
        }
    }
    
    pub fn trigger_event(&mut self, event: T) {
        self.events.push(event);
    }
    
    fn internal_action(&mut self) {
        self.trigger_event(T::from(Events::A));
        self.trigger_event(T::from(Events::B));
    }
    
    pub fn initialize(&mut self) {
        self.internal_action();
    }
    
    pub fn add(&mut self, event: T) {
        self.events.push(event);
    }
    
    pub fn any_events<TIn: Into<T>>(&self, event: TIn) -> bool {
        let custom_event:T = event.into();
        self.events.iter().any(|e| *e == custom_event)
    }
}

#[derive(PartialEq, Debug)]
enum AppEvents {
    A,
    B,
    C
}

impl From<Events> for AppEvents {
    fn from(value: Events) -> Self {
        match value {
            Events::A => AppEvents::A,
            Events::B => AppEvents::B,
        }
    }
}

fn main() {
  let mut foo = Foo::<AppEvents>::new();
  assert!(!foo.any_events(Events::A));
  assert!(!foo.any_events(Events::B));
  
  foo.initialize();
  foo.add(AppEvents::C);
 
  // Interact with foo using either internal or external events
  assert!(foo.any_events(AppEvents::C)); 
  assert!(foo.any_events(Events::A));
  assert!(foo.any_events(Events::B));
  
  println!("{:?}", foo);
}

Rust playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cf82e44ae4f438c0b3308b54ffb36ffa

Doug
  • 32,844
  • 38
  • 166
  • 222