为什么编译器不推断impl特质返回值的关联类型的具体类型?

时间:2018-10-16 08:48:53

标签: rust traits type-inference

我有一个具有相关类型的特征:

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())
    }
}

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

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

我知道在此示例中,我可以只使用Dog作为返回类型,但是在我的实际代码中,我必须使用impl Speak(实际上,上述函数是由宏生成的)

据我了解,impl Trait表示法使编译器可以确定实际返回的是哪种具体类型,因此我希望以下函数能够正确编译,因为speaker()返回了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函数的返回类型。

谁缺少某些东西,编译器还是我自己?

2 个答案:

答案 0 :(得分:6)

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

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

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

答案 1 :(得分:2)

你是。

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

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