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