【问题标题】:Type mismatch in trait bound - Why can't rustc infer the type on its own?特征绑定中的类型不匹配 - 为什么 rustc 不能自行推断类型?
【发布时间】:2015-09-03 03:23:32
【问题描述】:

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

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 错误:类型不匹配解析 &lt;collections::vec::Vec&lt;&amp;B&gt; as core::iter::IntoIterator&gt;::Item == &amp;Bar&lt;'_&gt;: 预期的类型参数, 找到特质栏 [E0271]

但是,将 vec![b] 更改为 vec![b as &amp;Bar] 可以正常工作。但既然b&amp;B 类型,而B 有一个Bar&lt;'a&gt; 界限,为什么编译器不能确定b 确实是一个&amp;Bar

【问题讨论】:

    标签: rust


    【解决方案1】:

    理论上它可以尝试将类型“修复”为&amp;Bar。但是,您在这里遇到了方差问题 - &amp;B 的向量不是 &amp;Bar 的向量,因为 &amp;B&amp;Bar,反之亦然。

    示例:假设我们将&amp;B 的向量视为&amp;Bar 的向量。现在我们有一个&amp;C 类型的值,其中&amp;C&amp;Bar 但不是&amp;B - 我们可以将&amp;C 放入我们的向量中吗?好吧,显然不是,因为实现是&amp;B 的向量。所以我们只能允许读,不能写。另一方面,我们可以尝试将&amp;Bar 的向量视为&amp;B 的向量——只要我们只将&amp;Bs 写入我们的向量(因为它允许接受&amp;Bs当然) - 但由于它仍然是&amp;Bar 的向量,它可以包含不是&amp;Bs 的东西,所以我们不允许从中读取。

    因此,允许同时读取和写入的容器需要在其通用参数中保持不变。在以这种方式同时具有多态性泛型的语言中,你总是会遇到这个问题。

    所以,回到实际的问题:既然存在这个问题,就不可能有一个自动逃生舱口将您最初被推断为&amp;B 的向量的表达式转换为@ 的类型向量987654342@。明确说明您的类型,这不会发生。


    完整类型推断也许在这里可以提供帮助,但我不确定在 rust 这样的语言中是否可以进行完整类型推断。

    【讨论】:

    • 这很有意义,谢谢。我原以为原因是这样的,但不能只说出来。
    • “多态性和泛型”我会说它最好写成“子类型和泛型”,因为泛型是一种多态性(即parametric)。
    • @VladimirMatveev 好点,但我想这里的大多数人都来自 C++/Java 背景并且理解“多态性”是指“子类型化”;不过,我认为这也不会让我们中间的haskellers更难阅读答案。
    猜你喜欢
    • 2021-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-04
    • 1970-01-01
    • 2018-10-08
    • 1970-01-01
    相关资源
    最近更新 更多