首页 > 解决方案 > 泛型加动态调度

问题描述

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

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

我天真的想法是:

  1. 使用Box<Rng>,但这不起作用,因为Rng它具有通用功能。
  2. StdRng在and上使用枚举XorShiftRng,但我真的想不出一个好的方法来写这个。

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

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

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`

标签: rusttraitstrait-objects

解决方案


你注意到你自己不能使用Box<dyn Rng>,因为Rngtrait 不是对象安全的。但是,rand板条箱为此提供了一个解决方案:每个 RNG 的基础都由 trait 提供RngCore,它是对象安全的,并且Box<dyn RngCore>Rng通过以下两个 trait 实现来实现:

第一个实现确保它Box<dyn RngCore>本身RngCore,而第二个实现Rng所有RngCore对象。实际上,您将能够调用trait 对象Rng上的所有方法RngCore,并且实现动态地分派到后台所需的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);

推荐阅读