【问题标题】:Why can impl trait not be used to return multiple / conditional types?为什么 impl trait 不能用于返回多个/条件类型?
【发布时间】:2018-08-24 09:37:39
【问题描述】:

我正在尝试获取随机数生成器。由于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 并直接返回thread_rng(),我不会收到上述错误消息。

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

【问题讨论】:

    标签: rust traits return-type


    【解决方案1】:

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

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

    在这种特定情况下,您可能想要的是像 Box<dyn RngCore> 这样的 trait 对象。

    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 中是可选的。

    【讨论】:

    • dyn 关键字在做什么?没有它也能正常工作
    • 从技术上讲,它是多余的。引入它是为了“澄清”代码的含义:dyn RngCore 是从 RngCore 特征派生的动态分派类型。 dyn 将在 Rust 的未来版本中被要求。这正是 Shepmaster 将编辑的那种东西,所以我不妨现在就结束它。
    【解决方案2】:

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

    另见:

    【讨论】:

      猜你喜欢
      • 2021-02-01
      • 2019-03-20
      • 1970-01-01
      • 2018-06-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多