`struct` from an `enum`?

I have an enum that I'm deserializing from a configuration file. I also have a bunch of structs implementing MyTrait. I want to have one variant of the enum for each struct that implements MyTrait, and I want to be able to create an instance of the correct struct from the enum variant. Example:

enum Types {
    Type1,
    Type2,
    Type3,
}

struct Type1Option {
    ...
} impl MyTrait for Type1Option {
    ...
}

struct Type2Option {
    ...
} impl MyTrait for Type2Option {
    ...
}

struct Type3Option {
    ...
} impl MyTrait for Type3Option {
    ...
}

#[test]
fn test_this_mess() {
    let type2 = Types::Type2;
    let should_be_type_2_option = type2.generate();
}

Is there a good way to do this?

If MyTrait is dyn compatible[1], yo could return a trait object from your Types::generate method:

impl Types {
    fn generate(&self) -> Box<dyn MyTrait> {
        match self {
            Self::Type1 => Box::new(Type1Option::default()),
            Self::Type2 => Box::new(Type2Option::default()),
            Self::Type3 => Box::new(Type3Option::default()),
        }
    }
}

  1. The concept used to be called object-safety not too long ago. ↩︎

1 Like

I've written enum_variant_type that helps generate structs for each variant:

use enum_variant_type::EnumVariantType;

#[derive(EnumVariantType, SomeTrait)]
pub enum MyEnum {
    // Generates: `#[derive(SomeTrait)] struct A;`
    #[evt(derive(SomeTrait))]
    A,
    #[evt(derive(SomeTrait))]
    B,
}

I'm not sure if what's there helps you do what you want, it's possible that you need enum_variant_type#4 as well.

Is the idea that the enum is always fieldless, but the variant of the type2 value determines the type of should_be_type_2_option?

The values Types::Type1, Types::Type2, and Types::Type3 all have the same type (Types). So for example if you have

let type1 = Types::Type1;
let type2 = Types::Type2;
let x = type1.generate();
let y = type2.generate();

there is no way for the types of x and y to be different based on the value (variant) of type1 or type2, enforced at compile time. Their types could be different if generate returns a generic (inferred by how x and y are used or annotated), but then the type is determined by the call site, not the value of type1 or type2.

You can instead perform runtime checking in various ways. It will have to have some failure path, since type checking is done at compile time.

(The dyn suggestion instead lets x and y be the same type by erasing the underlying type.)


The above points can also be summed up as: variable values[1] don't participate in the type system.

const integers can participate in the type system in limited ways, so technically there are some ways to get a const enum value to determine the type of a variable. But it's only applicable if your enum values can be const (and is pretty hacky to boot), and thus not really an improvement over call-site-determined type generics or just having different method names or such. You'd still be deciding the types at compile time at the call site, just based on a const instead of generics or naming.


  1. or "well-formed inhabited non-const values" or something, if one tried hard enough to nail the definition down ↩︎