I tried to compile this code (Playground):
trait Family<'a> {
type Out;
}
struct U32Family;
impl<'a> Family<'a> for U32Family {
type Out = u32;
}
trait Iterator {
type Item;
fn next<'s>(&'s mut self) -> <Self::Item as Family<'s>>::Out
where
Self::Item: Family<'s>;
}
struct Foo;
impl Iterator for Foo {
type Item = U32Family;
fn next<'s>(&'s mut self) -> <Self::Item as Family<'s>>::Out
where
Self::Item: Family<'s>,
{
0u32 // <-- in real code, this is somehow calculated
}
}
But sadly, it results in this error:
error[E0308]: mismatched types
--> src/main.rs:28:9
|
24 | fn next<'s>(&'s mut self) -> <Self::Item as Family<'s>>::Out
| ------------------------------- expected `<U32Family as Family<'s>>::Out` because of return type
...
28 | 0u32
| ^^^^ expected associated type, found u32
|
= note: expected type `<U32Family as Family<'s>>::Out`
found type `u32`
I really don't understand why. Obviously, in this code snippet, <U32Family as Family<'s>>::Out
is exactly u32
. But Rust seems to think that it's not always the same. Why? And how can I make it compile?
Some notes:
type Out: for<'a> Family<'a>;
. So that's not a workaround that works for me.Family
, everything works.Family<'s>
with Family<'static>
in the function signature, everything works.EDIT: I can work around this problem by adding:
impl U32Family {
fn from<'a>(v: u32) -> <Self as Family<'a>>::Out {
v
}
}
Then I can just say Self::Item::from(0u32)
in the body of next()
. (Playground)
I think it's clear why the error in next()
is gone: U32Family::from
always takes u32
as argument. Hardcoded. Never changing. The bigger question about this workaround is: why does the from()
method compile fine? So in from()
the compiler somehow knows that <Self as Family<'a>>::Out
is always u32
, but if I try the same in next()
, somehow the compiler doesn't understand that <Self::Item as Family<'s>>::Out
is u32
. Now I'm even more confused.
EDIT2: first, I suspected that specialization is the problem. For example, you might write:
impl Family<'static> for U32Family {
type Out = char;
}
Then of course, the compiler would be right in assuming that u32
is not always the same as <Self::Item as Family<'s>>::Out
for any 's
. However, I think this is not the problem.
First of all, impls that can be specialized need to be marked with the default
keyword. I did not do that, so I should be able to assume the associated type is in fact u32
(the RFC talks about something very similar). But additionally, specialization based on lifetimes is not allowed.
So by now I tend to think this is a compiler error. But I'd love to get another answer!
I think the problem is that it is a "coincidence" that <Self::Item as Family<'s>>::Out
is u32
for all 's
. The compiler can prove it for any 's
you want, but it can't even express the concept that it is true for all 's
.
The work-around you have found is the right approach: add a method to U32Family
which converts a u32
into a <Self as Family<'a>>::Out
. The body of the method is entirely inside the scope of 'a
, so the compiler can prove that the conversion is type-correct for that 'a
, and therefore that the method is type-correct. Then, at the call-site, you're telling the compiler to use its knowledge about the method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With