我刚刚在我的一些代码中遇到了一个问题,并设法将其修改为以下最小的例子:
use std::iter::IntoIterator;
use std::marker::PhantomData;
trait Bar<'a> {
fn compile_plz(&self) -> &'a str;
}
struct Foo<'a> {
ph: PhantomData<&'a str>
}
impl<'a> Bar<'a> for Foo<'a> {
fn compile_plz(&self) -> &'a str {
"thx"
}
}
fn do_something_with_bars<'a, I>(it: I) -> Result<(), ()> where I: IntoIterator<Item=&'a Bar<'a>> {
Ok(())
}
fn take_bar<'a, B>(b: &'a B) where B: Bar<'a> {
do_something_with_bars(vec![b]);
}
fn main() {
let f = Foo{ph: PhantomData};
take_bar(&f);
}
此操作失败,并显示以下错误:
23:5:23:27错误:类型不匹配解析
<collections::vec::Vec<&B> as core::iter::IntoIterator>::Item == &Bar<'_>
: 期望的类型参数, 发现特质吧[E0271]
但是,将vec![b]
更改为vec![b as &Bar]
可以正常工作。但由于b
的类型为&B
,而B
的{{1}}绑定为Bar<'a>
,为什么编译器无法确定b
确实是&Bar
1}}?
答案 0 :(得分:4)
理论上可以尝试将类型“修复”为&Bar
。但是,您在这里遇到了方差问题 - &B
的向量不是&Bar
的向量,只是因为&B
是&Bar
,而且它们都不是真。
示例:假设我们将&B
的向量视为&Bar
的向量。现在我们有&C
类型的值,其中&C
是&Bar
而不是&B
- 我们可以将&C
放入我们的向量中吗?嗯,显然不是,因为实现是&B
的向量。所以我们只能允许读取而不是写入。另一方面,我们可以尝试将&Bar
的向量视为&B
的向量 - 只要我们只将&B
写入向量(因为它是允许的),这样就可以正常工作接受&B
当然) - 但由于它仍然是&Bar
的向量,它可以包含不是&B
的内容,因此我们不允许从中读取
因此,同时允许读写的容器需要在其泛型参数中保持不变。您将始终以这种方式同时具有多态和泛型的语言中存在此问题。
所以,回到实际的问题:既然存在这个问题,就不会有一个自动的逃生舱口,它会使你的表达式最初被推断为&B
的向量到类型向量&Bar
。明确你的类型,这不会发生。
完整类型推断也许可以在这里提供帮助,但我不确定是否可以使用像rust这样的语言进行完全类型推断。