Returning a result of `impl trait` causes mismatched types error


#1

Please consider the following example:

trait T {
    fn init() -> Result<Self, ()> where Self: Sized;
}

struct A;

struct B;

impl T for A {
    fn init() -> Result<Self, ()> where Self: Sized {
        Ok(A{})
    }
}

impl T for B {
    fn init() -> Result<Self, ()> where Self: Sized {
        Ok(B{})
    }
}

fn get_t(b: bool) -> Result<impl T + Sized, ()> {
    if b {
        A::init()
    } else {
        B::init()
    }
}

fn main() {
    let t = get_t(true);
}

(Playground)

It causes the following error:

error[E0308]: mismatched types
  --> src/main.rs:27:9
   |
27 |         B::init()
   |         ^^^^^^^^^ expected struct `A`, found struct `B`
   |
   = note: expected type `std::result::Result<A, _>`
              found type `std::result::Result<B, _>`

cargo 1.32.0 (8610973aa 2019-01-02)
rustc 1.32.0 (9fda7c223 2019-01-16)


#2

As it should, because you’re trying to return values of two different types.

impl Trait resolves to one type that implements Trait, not all types that implements Trait. You need to either use Box<dyn Trait>, or something like the either crate.

Edit: as an addendum, this has come up so many times, it might be a good idea to have an expanded error message specifically for this case.


#3

The problem is that in my real project, the trait has associated types and I cannot return a trait object with different associated types.

trait T {
    type P;

    fn init() -> Result<Self, ()> where Self: Sized;
}

struct A;

struct B;

impl T for A {
    type P = String;

    fn init() -> Self {
        A{}
    }
}

impl T for B {
    type P = Vec<String>;

    fn init() -> Self {
        B{}
    }
}

fn get_t(b: bool) -> Box<dyn T> {
    Box::new(if b {
        A::init()
    } else {
        B::init()
    })
}

fn main() {
    let t = get_t(true);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0191]: the value of the associated type `P` (from the trait `T`) must be specified
  --> src/main.rs:27:26
   |
2  |     type P;
   |     ------- `P` defined here
...
27 | fn get_t(b: bool) -> Box<dyn T> {
   |                          ^^^^^ associated type `P` must be specified

error: aborting due to previous error

For more information about this error, try `rustc --explain E0191`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.


#4

Well, those types wouldn’t be compatible anyway, so you’ll probably have to redesign your code, assuming you can’t remove the associated type from the trait. The compiler just can’t ignore differences like that.