为什么不能使用impl trait返回多个/条件类型?

时间:2018-08-24 09:37:40

标签: rust traits return-type

我正在尝试获得一个随机数生成器。由于OsRng::new()可能会失败,因此如果需要,我想退回到thread_rng()

extern crate rand; // 0.6.5

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

fn rng() -> impl RngCore {
    match OsRng::new() {
        Ok(rng) => rng,
        Err(e) => thread_rng(),
    }
}

但是,我收到了我无法理解的错误消息:

error[E0308]: match arms have incompatible types
 --> src/lib.rs:6:5
  |
6 | /     match OsRng::new() {
7 | |         Ok(rng) => rng,
8 | |         Err(e) => thread_rng(),
  | |                   ------------ match arm with an incompatible type
9 | |     }
  | |_____^ expected struct `rand::rngs::OsRng`, found struct `rand::prelude::ThreadRng`
  |
  = note: expected type `rand::rngs::OsRng`
             found type `rand::prelude::ThreadRng`

为什么编译器期望rand::OsRng而不是RngCore的实现?如果我删除match并直接返回thread_rng(),我不会得到以上错误消息。

我不认为这是How do I return an instance of a trait from a method?的重复,因为另一个问题是关于如何可以从函数返回特征的问题,而这个问题是关于为什么,编译器不允许我返回特征,但希望我返回OsRng,这不是函数的返回类型。

2 个答案:

答案 0 :(得分:9)

impl Trait不等同于返回接口或基类对象。这是说“我不想写我要返回的特定类型的名称”的一种方式。您仍在返回单个特定类型的值。您只是不说哪种类型。

每个分支都返回不同的类型,因此出现了问题。仅仅实现相同的特征是不够的。

在这种情况下,您可能想要的是特征对象,例如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()),
    }
}

注意:如果您使用的是Rust的较早版本,则可能需要删除dyn关键字。在当前(2015)版本的Rust中,它是可选的。

答案 1 :(得分:3)

DK. has already explained why,但我想提供一种替代解决方法。

Conditionally iterate over one of several possible iterators中所述,如果两个特征类型都相同,则可以创建一个实现特征的枚举。例如:

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

either crate为基本特征提供了许多这类实现。

另请参阅: