为什么 impl trait 不能用于 return 多个/条件类型?

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

我正在尝试获取随机数生成器。由于 OsRng::new() 可能会失败,如果必须的话,我想退回到 thread_rng()

extern crate rand; // 0.5.5

use rand::{thread_rng, OsRng, 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::OsRng`, found struct `rand::ThreadRng`
  |
  = note: expected type `rand::OsRng`
             found type `rand::ThreadRng`

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

我不认为这是 的重复,因为另一个问题是询问 如何 一个人可以 return 一个特征函数,这个问题是关于 为什么 编译器不允许我 return 一个特征但要我 return 一个 OsRng 而不是函数的 return 类型。

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

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

在这种特定情况下,您可能需要的是像 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 中是可选的。

,但我想提供一个替代解决方法。

中所述,您可以创建一个枚举来实现一个特征,如果它的两个组件类型都实现的话。例如:

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 为基本特征提供了很多此类实现。

另请参阅: