Tengo un rasgo con un tipo asociado:

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

Una implementación de ese rasgo:

#[derive(Default)]
pub struct Dog;

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

Y una función que devuelve una instancia de esa implementación:

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

Sé que en este ejemplo podría usar Dog como tipo de retorno, pero en mi código real tengo que usar impl Speak en su lugar (la función anterior de hecho es generada por una macro).

Según tengo entendido, la notación impl Trait permite al compilador averiguar qué tipo concreto se devuelve realmente, por lo que esperaría que la siguiente función se compile correctamente porque speaker() devuelve un Dog y ese { {X3}} es del tipo ():

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

Patio

En cambio, obtengo el siguiente error:

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>`

Es como si el compilador no pudiera (en este punto) inferir el tipo de retorno de la función speaker.

¿A quién le falta algo, el compilador o yo mismo?

2
Pierre-Antoine 16 oct. 2018 a las 11:48

2 respuestas

La mejor respuesta

Utilice -> impl Speak<Error = ()> como el tipo de retorno de speaker().

El problema es que el compilador necesita, solo a partir de la firma, suficiente información para que la persona que llama pueda utilizar la función. Si solo devuelve impl Speak, entonces el compilador sabe que speak() devuelve un Result<String, ???>; el tipo de error no se conoce y, por lo tanto, el compilador emite un error.

El compilador no puede inferir nada aquí. No puede inferir el tipo de error del sitio de la llamada, porque impl Trait en la posición de retorno no permite la inferencia del sitio de la llamada. No puede inferir el tipo de error de la implementación, porque eso significaría si las verificaciones de tipo de la persona que llama depende de la implementación, y no es así como funciona impl Trait. La persona que llama siempre debe verificar el tipo en presencia solo de la información de la firma; el tipo de hormigón solo se conecta después de eso.

9
Sebastian Redl 16 oct. 2018 a las 08:57

Usted está.

Nunca especificaste el tipo Error asociado, por lo que no puedes asumir nada al respecto. Incluso si realmente es (), el compilador no le permitirá usar ese conocimiento. Para resolver esto, simplemente especifique qué es Error:

pub fn speaker() -> impl Speak<Error = ()> {
    Dog::default()
}
2
DK. 16 oct. 2018 a las 08:55