trait HasFields {
type a;
type b;
type c;
type d;
}
pub struct Foo <T: HasFields, f: FieldSelector> {
inner: T::f
}
^-- is there a way to do something like this? valid values for f would be a, b, c, d
basically i want to pass in two generics, one is a type which satisfies the trait (we can do this already); another is a "selector" on the trait (I don't know how to do this)
is this possible, if so how? it not, is there a name for this feature
You need to define a separate trait to act as the type level function between a selector type and the field type. (In the future when we have general const generics, the selector could be an enum instead of four separate types.)
pub trait HasFields {
type A;
type B;
type C;
type D;
}
pub trait Select<F> {
type Out;
}
pub struct SelA;
pub struct SelB;
pub struct SelC;
pub struct SelD;
impl<T: HasFields> Select<SelA> for T { type Out = T::A; }
impl<T: HasFields> Select<SelB> for T { type Out = T::B; }
impl<T: HasFields> Select<SelC> for T { type Out = T::C; }
impl<T: HasFields> Select<SelD> for T { type Out = T::D; }
pub struct Foo<T: HasFields + Select<F>, F> {
inner: <T as Select<F>>::Out,
}
Note that you might want to eliminate HasFields entirely, and just have the implementors directly implement Select four times. This might be usefully more flexible and simplify trait bounds, or it might complicate the job of implementors without any benefits, depending on the situation.
Could I trouble you to write the code without HasFields ? The part I don't understand is -- once you remove HasFields, what do you replace T::A, T::B, T::C, T: with ?
That's hard to do because the generic implementations go away, but it might be like this (supposing that Something would have implemented HasFields):
struct Something {
a: i32, // I assume that the implementor should in fact have a field…
}
impl Select<SelA> for Something { type Out = i32; }
// ... and more for the other selectors
In the places you would write T::A, you write <T as Select<SelA>>::Out instead; and a T: HasFields bound becomes
Of course, this is more verbose in each step, but it means the selection doesn't need a trait that lists every type; as I said, it depends on which tradeoff you want. The main point is that HasFields itself it's not actually necessary.