I have a trait Super
that is generic over type parameter M
and two structs S1
and S2
that are generic over M
and implement Super
.
All of these are part of a library. I could seal the trait so that the user of the library cannot implement the trait on its own.
The Super
trait has a method call
that is generic over type parameters M2
and S
and takes a FnOnce
closure as argument. The closure takes S
as parameter. Currently, there is the trait bound S
: Super<M2>
.
trait Super<M> {
fn call<M2, S>(&self, f: impl FnOnce(S))
where
S: Super<M2>;
}
struct S1<M1>(PhantomData<M1>);
impl<M> Super<M> for S1<M> {
fn call<M2, S>(&self, f: impl FnOnce(S))
where
S: Super<M2>,
{
todo!()
}
}
struct S2<M>(PhantomData<M>);
impl<M1> Super<M1> for S2<M1> {
fn call<M2, S>(&self, f: impl FnOnce(S))
where
S: Super<M2>,
{
todo!()
}
}
This allows me to do, for example:
let s1 = S1::<u32>(PhantomData::default());
s1.call::<u64, S1<u64>>(|inner| {}); // inner is S1<64>
s1.call::<u64, S2<u64>>(|inner| {}); // inner is S2<64>
Question
I would like to modify the Super
trait so that the type of the parameter of the S
of closure is restricted to be the type that is implementing the Super
trait but generic over M2
instead of M
.
For the sake of example (I know that this doesn't work), I want this:
trait Super<M> {
fn call<M2, S>(&self, f: impl FnOnce(S))
where
S: Self<M2>;
}
After this modification, the example should behave as follows:
let s1 = S1::<u32>(PhantomData::default());
s1.call::<u64, S1<u64>>(|inner| {}); // inner is S1<64>. OK, as it can be whatever S1<M2>.
s1.call::<u64, S2<u64>>(|inner| {}); // does not compile: inner is an S2<M2> and not an S1<M2>.
Bonus
As I said at the beginning of the post, the only types implementing Super
are S1
and S2
.
Thus, in principle, the signature of the call
method should not even require the S
generic type parameter: with S1
, then it is implied to be S1<M2>
; with S2
, then it is implied to be S2<M2
>.