Why can impl trait not be used to return multiple / conditional types?

Solution 1:

impl Trait is not equivalent to returning an interface or base class object. It's a way of saying "I don't want to write the name of the specific type I'm returning". You're still returning a value of a single, specific type; you just aren't saying which type.

Each of those branches is returning different types, hence the problem. Implementing the same trait is not enough.

What you likely want in this specific case is a trait object like Box<dyn RngCore>.

extern crate rand; // 0.6.5

use rand::{rngs::OsRng, thread_rng, RngCore};

fn rng() -> Box<dyn RngCore> {
    match OsRng::new() {
        Ok(rng) => Box::new(rng),
        Err(_) => Box::new(thread_rng()),
    }
}

Note: if you are using a slightly older version of Rust, you may need to remove the dyn keyword. It's optional in the previous (2015) edition of Rust.

Solution 2:

DK. has already explained why, but I'd like to provide an alternative workaround.

As mentioned in Conditionally iterate over one of several possible iterators, you can create an enum that implements a trait if both of its component types do. For example:

extern crate rand; // 0.6.5

use rand::{rngs::OsRng, thread_rng, RngCore};

fn rng() -> impl RngCore {
    match OsRng::new() {
        Ok(rng) => EitherRng::Left(rng),
        Err(_) => EitherRng::Right(thread_rng()),
    }
}

enum EitherRng<L, R> {
    Left(L),
    Right(R),
}

impl<L, R> RngCore for EitherRng<L, R>
where
    L: RngCore,
    R: RngCore,
{
    fn next_u32(&mut self) -> u32 {
        match self {
            EitherRng::Left(l) => l.next_u32(),
            EitherRng::Right(r) => r.next_u32(),
        }
    }

    fn next_u64(&mut self) -> u64 {
        match self {
            EitherRng::Left(l) => l.next_u64(),
            EitherRng::Right(r) => r.next_u64(),
        }
    }

    fn fill_bytes(&mut self, b: &mut [u8]) {
        match self {
            EitherRng::Left(l) => l.fill_bytes(b),
            EitherRng::Right(r) => r.fill_bytes(b),
        }
    }

    fn try_fill_bytes(&mut self, b: &mut [u8]) -> Result<(), rand::Error> {
        match self {
            EitherRng::Left(l) => l.try_fill_bytes(b),
            EitherRng::Right(r) => r.try_fill_bytes(b),
        }
    }
}

The either crate provides a lot of these types of implementations for fundamental traits.

See also:

  • How do I conditionally return different types of futures?