我尝试编译这段代码 (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
}
}
但遗憾的是,它导致了这个错误:
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`
我真的不明白为什么。显然,在这段代码中,<U32Family as Family<'s>>::Out
就是 u32
。但 Rust 似乎认为它并不总是相同的。为什么?我该如何让它编译通过?
一些注释:
- 有很多类似的情况会出现类似的错误,但我认为这与我之前看到的所有情况都不同。
- 我不能使用
type Out: for<'a> Family<'a>;
。所以这不是适合我的解决方法。 - 如果我删除
Family
的生命周期参数,一切都正常。 - 如果我在函数签名中将
Family<'s>
替换为Family<'static>
,一切都正常。
编辑: 我可以通过添加以下内容来解决此问题:
impl U32Family {
fn from<'a>(v: u32) -> <Self as Family<'a>>::Out {
v
}
}
然后我就可以在next()
的主体中写Self::Item::from(0u32)
。(Playground)
我认为大家都清楚了,为什么next()
中的错误消失了:因为U32Family::from
总是以为参数。 硬编码。 永远不会改变。 关于这个解决方法的更大问题是:为什么from()
方法能够编译通过? 所以,在from()
中,编译器以某种方式知道<Self as Family<'a>> :: Out
始终是u32
,但如果我尝试在next()
中做同样的事情,那么编译器就无法理解<Self :: Item as Family<'s>> :: Out
是u32
。 现在我更加困惑了。
EDIT2:首先,我怀疑专业化是问题所在。 例如,您可以编写:
impl Family<'static> for U32Family {
type Out = char;
}
当然,编译器会正确地假设对于任何 's
,u32
并不总是与 <Self::Item as Family<'s>>::Out
相同。 但是,我认为这不是问题的所在。
首先,可以进行特化的impl 需要用关键字 default
进行标记。我没有这样做,因此我应该能够假设关联类型实际上是 u32
(RFC 讨论了非常相似的内容)。但另外,基于生命周期的特化不被允许。
所以现在我倾向于认为这是编译器错误。但我很想得到另一个答案!
trait FamilyBase { type Out; }
用于定义类型,以及trait Family<'a> : FamilyBase { }
用于实际操作。可能有点hacky... - rodrigoOut
类型中使用生命周期参数,比如impl<'a> Family<'a> for Bar { type Out = &'a bool; }
。这在你提出的两个 trait 的想法中是不可能的 :/ - Lukas Kalbertodttrait Family
中添加此函数:fn zero() -> Self::Out;
,然后在Iterator::next()
中调用<Self::Item as Family<'s>>::zero()
。 - rodrigonext
函数转移到整个Iterator
特质(即type Item: Family<'a>
),则可以使其编译。但这确实需要在Iterator
中添加一个生命周期。Playground - Kwarrtz