【问题标题】:Why does the compiler not infer the concrete type of an associated type of an impl trait return value?为什么编译器不推断 impl trait 返回值的关联类型的具体类型?
【发布时间】:2019-03-20 18:03:17
【问题描述】:

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

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 函数的返回类型。

谁错过了什么,编译器还是我自己?

【问题讨论】:

    标签: rust traits type-inference


    【解决方案1】:

    使用-&gt; impl Speak&lt;Error = ()&gt;作为speaker()的返回类型。

    问题在于编译器需要仅从签名中获取足够的信息,以便调用者可以实际使用该函数。如果你只返回impl Speak,那么编译器就知道speak() 返回一个Result&lt;String, ???&gt;——错误类型未知,因此编译器会发出错误。

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

    【讨论】:

    • 您写道:“它无法从实现中推断出类型”。真的吗?当“impl Trait”被引入时,我明白它可以让你避免使用 Trait 对象,所以我假设实际类型在编译时被解析......因此我期望编译器会正确推断类型。跨度>
    • @Pierre-Antoine 实际类型在编译时解析,但在类型检查之后speaker() 的调用者必须仅根据签名中给出的信息进行类型检查,即 impl Speak 没有错误类型的信息。
    • 哦,那是有道理的。谢谢。
    • 另外,我当然可以写“-> Speak”,或者甚至是“-> Dog”。但是正如我(试图)在我的问题中解释的那样,这不是我的实际代码中的一个选项,因为“扬声器”函数是由宏生成的,并且该宏没有关于实际类型的信息......我想我有以某种方式忍受这一点,或者找到一种方法将该信息提供给宏。
    • @Pierre-Antoine 也许提出一个新问题,更现实地减少您的根本问题?你提出的设计有些奇怪,一方面你不希望函数指定结果类型,另一方面你希望调用者能够假设它。
    【解决方案2】:

    你是。

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

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

    【讨论】:

    • 请参阅上面我对@sebasitan-redl 的第三条评论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-01
    • 2018-08-22
    • 1970-01-01
    相关资源
    最近更新 更多