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

问题描述 投票:6回答:2

我想要一个随机数生成器。由于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,它不是函数的返回类型。

rust traits return-type
2个回答
11
投票

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版本中的可选项。


6
投票

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为基本特征提供了许多这些类型的实现。

也可以看看:

© www.soinside.com 2019 - 2024. All rights reserved.