简体   繁体   中英

How to make a struct containing an enum generic over the enum variant?

Please have a look at the code:

struct Human {
  name: String,
  profession: Profession,
}

enum Profession {
  Doctor,
  Teacher
}

struct Family {
  family_doctor: // How to express type that will mean Human (because name matters) of profession Doctor?
}

Is it possible to do that, by making Human generic somehow, and pass variant Doctor as a type parameter for profession field? If not, then what is the closest workaround for such relationships you would suggest?

Please note that this question may seem as a duplicate of an older one. But firstly, Rust keeps evolving, secondly, I am asking for a workaround.

Probably the easiest thing to do is to make Profession a trait rather than an enum , and make each Profession a unit struct:

struct Human<P: ?Sized + Profession> {
  name: String,
  profession: P,
}

struct Doctor;
struct Teacher;

trait Profession {}

impl Profession for Doctor {}
impl Profession for Teacher {}

struct Family {
  family_doctor: Human<Doctor>,
}

Most functions that accept Humans can be expressed using generics or impl Profession :

fn into_name<P: Profession>(any_human: Human<P>) -> String {
    any_human.name
}

Another way of writing functions that accept different kinds of Human is to use dynamic dispatch. If you do it this way, any access to a Human has to be done through a pointer, like Box<Human<dyn Profession>> or &Human<dyn Profession> (note this works because of P: ?Sized in the definition of Human ):

fn into_name_dyn(any_human: Box<Human<dyn Profession>>) -> String {
    any_human.name
}

Yet another possibility is to implement Profession for Box<dyn Profession> and use that as the type parameter. This puts the pointer inside Human so only the Profession is behind it:

impl<P: ?Sized + Profession> Profession for Box<P> {}

fn into_name_box(any_human: Human<Box<dyn Profession>>) -> String {
    any_human.name
}

If you still want the enum , here's one suggestion: put the structs inside same-named variants. This way when you add behavior to the Profession trait, you can implement Profession for SomeProfession by deferring to the inner type's implementation. The enum_derive crate can make this process easier.

enum SomeProfession {
    Doctor(Doctor),
    Teacher(Teacher),
}

impl Profession for SomeProfession {}

fn into_name_enum(any_human: Human<SomeProfession>) -> String {
    any_human.name
}

See also

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM