Search code examples
genericsrusttraitstyping

How to tell compiler that inner field implements traits or that generic trait has only 2 variants?


I have 2 low-level types, State and Base. States can be observed, but depending on the viewpoint, some see the full State and other only see a Base.

State constitutes the basis for higher-level types, but I cannot find a way to typecheck this ambivalent observe behaviour.

Here is a boiled down attempt to implement it with genericity and a simple impl trait bound:

// Those two things can be observed..
struct State;
struct Base;

// .. but from 2 different views.
trait Observable<Obs> {
    fn observe(&self) -> Obs;
}
// Some view states as states.
impl Observable<State> for State {
    fn observe(&self) -> State {
        State {}
    }
}
// Some view states as bases.
impl Observable<Base> for State {
    fn observe(&self) -> Base {
        Base {}
    }
}

// States serve as basis for other types..
struct Container {
    field: State,
} // .. there are quite a few like this one.

// Now, when it's time to observe the inner state of the container..
fn process<Obs>(state: &impl Observable<Obs>, container: &Container) -> (Obs, Obs) {
    (
        state.observe(), // (this typechecks fine)
        // .. how to annotate this so the compiler is confident that
        // container.field *does* implements Observable<Obs>?
        container.field.observe(),
    )
}

fn main() {
    // Build up data.
    let s = State {};
    let c = Container { field: State {} };
    // And observe it.
    let o_full: (State, State) = process(&s, &c);
    let o_partial: (Base, Base) = process(&s, &c);
}

yielding the error

error[E0277]: the trait bound `State: Observable<Obs>` is not satisfied
  --> src/main.rs:33:25
   |
33 |         container.field.observe(),
   |                         ^^^^^^^ the trait `Observable<Obs>` is not implemented for `State`

I am confident that this can be achieved with generics and monomorphization because everything is statically known.

I also sort of understand why the compiler is worried that Observe<Obs> may not be implemented on State for any Obs type.

But the compiler is missing informations here, because I still know more than it does:

  • How to tell the compiler that the <Obs> generic type will only ever be State or Base?

  • How make it obvious to the compiler that the second parameter of process is of type Container, so container.field is of type State and this does implement Observable<Obs>?


Solution

  • Maybe I miss something, but couldn't you just state that State implements Observable<Obs>? I.e.:

    fn process<Obs>(state: &impl Observable<Obs>, container: &Container) -> (Obs, Obs)
        where State: Observable<Obs> // <--- tell compiler that State satisfies Observable<Obs>
    {
        (
            state.observe(),
            container.field.observe(),
        )
    }