rust - 泛型加动态调度
问题描述
考虑一下我有一个make_numbers
应该创建一串随机数的函数,但是我想在运行时(用户输入)决定应该使用哪种随机数生成器的情况。为了使它更加困难,让我们假设该make_numbers
函数对要生成的数字类型是通用的。
我用伪代码写了我想要实现的东西,我明白为什么这不起作用。但是,我不知道 Rust 中的惯用方式会是什么样子来实现这一点?
我天真的想法是:
- 使用
Box<Rng>
,但这不起作用,因为Rng
它具有通用功能。 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`
解决方案
你注意到你自己不能使用Box<dyn Rng>
,因为Rng
trait 不是对象安全的。但是,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);
推荐阅读
- debugging - 在调试时,IDE 如何识别代码序列。整个工作流程是否可以像类图一样以图形方式查看?
- variables - 默认情况下局部变量是否可训练?
- python - Django 1.11.20 获取每 ISO 周和年的行数
- rx-swift - 具有转换的 BehaviorSubject
- android - Android Kotlin - 捕获的异常仍然会使程序崩溃
- html - 让按钮只在点击一定次数后才做某事?
- javascript - 将 Props 绑定到状态 ReactJS
- c++ - 我想要 grpc 版本 1.14.1 的 protobuff
- python - 如何将字符串格式作为变量传递给 f 字符串
- android - 在不解析 json 数据的情况下,以任何方式存储 JSONArray