特征界限中的类型不匹配 - 为什么不能自己推断类型?

时间:2015-09-03 03:23:32

标签: rust

我刚刚在我的一些代码中遇到了一个问题,并设法将其修改为以下最小的例子:

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}}?

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这样的语言进行完全类型推断。