考虑一下我有一个函数 make_numbers 应该创建一串随机数的情况,但是我想在运行时(用户输入)决定应该使用哪种随机数生成器。为了让它变得更加困难,让我们假设 make_numbers 函数对于要生成的数字类型是通用的。

我用伪代码写了我想实现的目标,我明白为什么这不起作用。但是,我不知道 Rust 中的惯用方式会是什么样子来实现这一目标?

我天真的想法是:

  • 使用 Box<Rng> ,但这不起作用,因为 Rng 具有通用功能。
  • StdRngXorShiftRng 上使用枚举,但我真的想不出写这个的好方法。

  • 你能给我一些关于这个特定问题的好的解决方案的提示吗?

    注意:这个问题不是关于具有不同类型的不同匹配臂(解决方案可以是 Box 或 enum,如上所述)-而是如何在这种情况下应用这些解决方案。

    extern crate rand;
    
    use rand::{Rng, SeedableRng, StdRng};
    use rand::prng::XorShiftRng;
    use std::string::String;
    use rand::distributions::{Distribution, Standard};
    use std::fmt::Display;
    
    // Generic function that should work with any type of random number generator
    fn make_numbers<T, R: Rng>(rng: &mut R) -> String
        where T: Display, Standard: Distribution<T>
    {
        let mut s = String::new();
        for _i in 0..10 {
            s.push_str(format!("_{}", rng.gen::<T>()).as_str());
        }
        s
    }
    
    fn main() {
        let use_std = true; // -> assume that this will be determined at runtime (e.g. user input)
    
        // Pseudo code, will not work.
        let mut rng = match use_std {
            true => StdRng::from_seed(b"thisisadummyseedthisisadummyseed".to_owned()),
            false => XorShiftRng::from_seed(b"thisisadummyseed".to_owned())
        };
    
        let s = make_numbers::<u8>(&mut rng);
    
        // ... do some complex stuff with s ...
    
        print!("{}", s)
    }
    

    error[E0308]: match arms have incompatible types
      --> src/main.rs:24:19
       |
    24 |       let mut rng = match use_std {
       |  ___________________^
    25 | |         true => StdRng::from_seed(b"thisisadummyseedthisisadummyseed".to_owned()),
    26 | |         false => XorShiftRng::from_seed(b"thisisadummyseed".to_owned())
       | |                  ------------------------------------------------------ match arm with an incompatible type
    27 | |     };
       | |_____^ expected struct `rand::StdRng`, found struct `rand::XorShiftRng`
       |
       = note: expected type `rand::StdRng`
                  found type `rand::XorShiftRng`
    

    最佳答案

    您注意到自己不能使用 Box<dyn Rng>,因为 Rng 特性不是对象安全的。 rand crate 为此提供了一个解决方案:每个 RNG 的基础都是由 trait RngCore 提供的,它是对象安全的,并且 Box<dyn RngCore> 也通过这两个 trait 实现来实现 Rng:

  • impl<R: RngCore + ?Sized> RngCore for Box<R>
  • impl<R: RngCore + ?Sized> Rng for R

  • 第一个实现确保 Box<dyn RngCore>RngCore 本身,而第二个实现为所有 Rng 对象实现 RngCore。实际上,您将能够调用 Rng trait 对象上的所有 RngCore 方法,并且该实现会在后台动态分派(dispatch)到所需的 RngCore 方法。

    利用这一点,您可以使用以下代码:
    let mut rng: Box<dyn RngCore> = if use_std {
        Box::new(
            StdRng::from_seed(b"thisisadummyseedthisisadummyseed".to_owned())
        )
    } else {
        Box::new(
            XorShiftRng::from_seed(b"thisisadummyseed".to_owned())
        )
    };
    let s = make_numbers::<u8, _>(&mut rng);
    

    关于rust - 泛型加上动态调度,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53030440/

    10-10 18:34