为什么编译器不推断 impl trait return 值的关联类型的具体类型?

Why does the compiler not infer the concrete type of an associated type of an impl trait return value?

我有一个关联类型的特征:

pub trait Speak {
    type Error;
    fn speak(&self) -> Result<String, Self::Error>;
}

该特征的实现:

#[derive(Default)]
pub struct Dog;

impl Speak for Dog {
    type Error = ();
    fn speak(&self) -> Result<String, Self::Error> {
        Ok("woof".to_string())
    }
}

还有一个函数 returning 该实现的一个实例:

pub fn speaker() -> impl Speak {
    Dog::default()
}

我知道在这个例子中我可以只使用 Dog 作为 return 类型,但在我的实际代码中我必须使用 impl Speak 代替(上面的函数在宏生成的事实)。

据我了解,impl Trait 表示法让编译器找出具体类型实际上是 returned,所以我希望以下函数能够正确编译,因为 speaker() returns a Dog 并且 Dog::Error 是类型 ():

fn test() -> Result<String, ()> {
    speaker().speak()
}

playground

相反,我收到以下错误:

error[E0308]: mismatched types
  --> src/lib.rs:21:5
   |
20 | fn test() -> Result<String, ()> {
   |              ------------------ expected `std::result::Result<std::string::String, ()>` because of return type
21 |     speaker().speak()
   |     ^^^^^^^^^^^^^^^^^ expected (), found associated type
   |
   = note: expected type `std::result::Result<_, ()>`
              found type `std::result::Result<_, <impl Speak as Speak>::Error>`

好像编译器无法(此时)推断出 speaker 函数的 return 类型。

谁遗漏了什么,编译器还是我自己?

你是。

您从未指定关联的 Error 类型,因此您不能对其进行任何假设。即使它确实是 (),编译器也不允许您使用该知识。要解决这个问题,只需指定 Error 是什么:

pub fn speaker() -> impl Speak<Error = ()> {
    Dog::default()
}

使用-> impl Speak<Error = ()>作为speaker()的return类型。

问题是编译器仅从签名中就需要调用者可以实际使用该函数的足够信息。如果您只是 return impl Speak,那么编译器会知道 speak() return 是 Result<String, ???> - 错误类型未知,因此编译器会发出一个错误。

编译器无法在此处推断出任何内容。它无法从调用站点推断错误类型,因为 return 位置中的 impl Trait 不允许从调用站点推断。它无法从实现中推断错误类型,因为这意味着调用者类型检查是否取决于实现,而这不是 impl Trait 的工作方式。调用者必须始终在仅存在签名信息的情况下进行类型检查;之后才插入具体类型。