Consider the following struct and trait:
use std::marker::PhantomData;
struct Base<M, T>(PhantomData<M>, PhantomData<T>);
trait Super<M> {
type T;
fn call<M2>(&self, f: impl FnOnce(Base<M2, Self::T>))
where
Base<M2, Self::T>: Super<M2>;
}
impl<M> Super<M> for Base<M, ()> {
type T = ();
fn call<M2>(&self, f: impl FnOnce(Base<M2, Self::T>))
where
Base<M2, Self::T>: Super<M2>,
{
todo!()
}
}
I can write a test function that takes a generic Super
, and call its call
method:
fn takes_super<M, S>(s: S)
where
S: Super<M>,
{
s.call::<u32>(|outer| {
outer.call::<u8>(|inner| {
//
});
});
}
However, it does not compile due to unsatisfied trait bound errors:
Error 1
error[E0277]: the trait bound `Base<u32, <S as Super<M>>::T>: Super<u32>` is not satisfied
--> src/main.rs:28:7
|
28 | s.call::<u32>(|outer| {
| ^^^^ the trait `Super<u32>` is not implemented for `Base<u32, <S as Super<M>>::T>`
|
note: required by a bound in `Super::call`
--> src/main.rs:10:28
|
8 | fn call<M2>(&self, f: impl FnOnce(Base<M2, Self::T>))
| ---- required by a bound in this associated function
9 | where
10 | Base<M2, Self::T>: Super<M2>;
| ^^^^^^^^^ required by this bound in `Super::call`
help: consider extending the `where` clause, but there might be an alternative better way to express this requirement
|
26 | S: Super<M>, Base<u32, <S as Super<M>>::T>: Super<u32>
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Error 2
error[E0599]: no method named `call` found for struct `Base` in the current scope
--> src/main.rs:29:15
|
3 | struct Base<M, T>(PhantomData<M>, PhantomData<T>);
| ----------------- method `call` not found for this struct
...
29 | outer.call::<u8>(|inner| {
| ------^^^^ method not found in `Base<u32, <S as Super<M>>::T>`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following traits define an item `call`, perhaps you need to implement one of them:
candidate #1: `Super`
candidate #2: `Fn`
I can fix the first error by modifying the trait bound of the test function:
fn takes_super<M, S>(s: S)
where
S: Super<M>,
Base<u32, <S as Super<M>>::T>: Super<u32>, // new trait bound
{
s.call::<u32>(|outer| {
outer.call::<u8>(|inner| {
//
});
});
}
The second error, which I don't know how to fix, becomes:
Error
error[E0277]: the trait bound `Base<u8, <Base<u32, <S as Super<M>>::T> as Super<u32>>::T>: Super<u8>` is not satisfied
--> src/main.rs:30:15
|
30 | outer.call::<u8>(|inner| {
| ^^^^ the trait `Super<u8>` is not implemented for `Base<u8, <Base<u32, <S as Super<M>>::T> as Super<u32>>::T>`
|
= help: the trait `Super<u8>` is implemented for `Base<u8, ()>`
= help: for that trait implementation, expected `()`, found `<Base<u32, <S as Super<M>>::T> as Super<u32>>::T`
note: required by a bound in `Super::call`
--> src/main.rs:10:28
|
8 | fn call<M2>(&self, f: impl FnOnce(Base<M2, Self::T>))
| ---- required by a bound in this associated function
9 | where
10 | Base<M2, Self::T>: Super<M2>;
| ^^^^^^^^^ required by this bound in `Super::call`
Is there a way to solve these problems all together, perhaps even modifying the Super
trait?
It is tedious to add the Base<u32, <S as Super<M>>::T>: Super<u32>
bound.
Ideally, the test function should just take a S: Super<M>
, without additional bounds.
Importantly, outer
and inner
should always be parametrized by the same Self::T
.