0

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>?

iago-lito
  • 3,098
  • 3
  • 29
  • 54
  • *without genericity propagating across all my project* — that's how it works. It looks like your question might be answered by the answers of [How can I avoid a ripple effect from changing a concrete struct to generic?](https://stackoverflow.com/q/44912349/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Apr 07 '20 at 14:17
  • @Shepmaster *that's how it works* --- aarh too bad :') Thank you for the pointer, I'm getting into it and will update asap. – iago-lito Apr 07 '20 at 14:19
  • @Shepmaster Well, although both posts complaint about "generics spreading everywhere", I'm not sure the situation is the same. The other post implements a fixed `trait A` on a generic `struct B` while I need implement variants of generic `trait A` on fixed `struct B`. I will edit the post so it focuses more on the *" how to tell the compiler that.."* aspects. – iago-lito Apr 07 '20 at 14:35

1 Answers1

1

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(),
    )
}
phimuemue
  • 34,669
  • 9
  • 84
  • 115
  • Awesome! Thank you :D For some reason, I was just convinced that types listed in `where` clauses could only be generic types (so `T` or here `Obs`) but not concrete types (like `State`). Where is this documented? – iago-lito Apr 07 '20 at 15:49
  • In addition, then, note that the first parameter only needs be typed with `State` :D – iago-lito Apr 07 '20 at 15:50