I have 2 low-level types, State
and Base
. State
s can be observe
d, 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>
?
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(),
)
}