How to define a enum with one variant for extension?

I have a enum Value and now I want to add a variant Ext for extension. I have the following requirements:

  • Value must implement Clone, PartialEq, Eq, Hash
  • Minimize the adjustment of current code

The obvious way is to use generic type, but this way doesn't satisfy the second requirement, it affects nearly every code. I have to add generic type parameter everywhere in my crate.

trait ValueExt: Clone + PartialEq + Eq + Hash {}

#[derive(Clone, PartialEq, Eq, Hash)]
enum Value<T: ValueExt> {
    Unit,
    Bool(bool),
    Ext(T),
}

The second thought is to use dyn trait, but for a trait to be object safe, it can't use Clone, PartialEq, Hash as its super trait. So I need to define a werid trait and implement Clone, PartialEq, Hash manually.

trait ValueExt {
    fn clone(&self) -> Value;
    fn eq(&self, other: &Value) -> bool;
    fn hash(&self) -> u64;
}

enum Value {
    Unit,
    Bool(bool),
    Ext(Box<dyn ValueExt>),
}

impl Clone for Value { xxx }
impl PartialEq for Value { xxx }
impl Hash for Value { xxx }

What I want is something like an associated type, (the code below is not valid)

trait ValueExt: Clone + PartialEq + Eq + Hash {}
enum Value {
    type VE: ValueExt;
    Unit,
    Bool(bool),
    Ext(VE),
}

// somewhere in user's code
struct VEImpl;
impl ValueExt for VEImpl { xxx }
type Value::VE = VEImpl;

I searched on google, there are something related, but they can't solve my problem.

  • extern type, which I can't specify a trait bound.
  • impl trait type alias, i.e. type VE = impl ValueExt;, which can't be used as a field in a struct.

Any ideas?

You can give the type parameter a default, which lets you omit it when naming the type (minimizing adjustment, as you say). That default could be a type with no values:

#[derive(Clone, PartialEq, Eq, Hash)]
enum Value<T: ValueExt = NoExt> {
    Unit,
    Bool(bool),
    Ext(T),
}

#[derive(Clone, PartialEq, Eq, Hash)]
enum NoExt {}

Then Value is still valid, but doesn't allow any Ext, and you instead write Value<Something> when you want to use the extension.

4 Likes

But if I want implement some functions on Value, I need to implement them for generic Value<T>, otherwise Value<ExtImpl> can't use these functions. I have a lot of functions dealing with the common part of Value, so I have to rewrite all of them to make them working with extensions.

Here's the dyn ValueExt approach, implemented in such a way that Box<dyn ValueExt> implements all the desired traits so you can just derive them on the enum.

3 Likes

Thank you! Your solution is much cleaner than mine.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.