I'm starting with a regular &mut ThreadRng
in all my functions, but it limits dependency injection capacities. There are SeedableRng
ofc, but how to use it in places where initially it was a ThreadRng
?
Coming from other languages I'd like to specify some interface in the fn declaration, but &mut dyn Rng
doesn't work because Rng
is not object safe.
What I ended up with is passing &mut StdRng
everywhere and
let mut rng = match config.seed {
Some(seed) => StdRng::seed_from_u64(seed),
_ => StdRng::from_rng(rand::thread_rng()).unwrap_or(StdRng::from_entropy())
};
But it's awkward to init 1 Rng
only to setup another Rng
I also searched the pattern StdRng::from_rng(rand::thread_rng())
and it's just 56 samples in github, from 141k of using rand::thread_rng()
So what's the best signature for a fn accepting Rng and the best way to init with an optional seed?
p.s. I don't care about wasm/etc runtimes, it's just a regular server-side app
The rand
documentation has a bit about generic usage in it you can find the following:
Since
Rng: RngCore
and everyRngCore
implementsRng
, it makes no difference whether we useR: Rng
orR: RngCore
From that we can deduce, that we can just use the object safe RngCore
in place of Rng
in situations that require dynamic dispatch:
use rand::{Rng, RngCore};use rand::rngs::StdRng;
fn main() {
let seed = None;
let mut std_rng;
let mut thread_rng;
let rng: &mut dyn RngCore = match seed {
Some(seed) => {
std_rng = StdRng::seed_from_u64(seed);
&mut std_rng
}
_ => {
thread_rng = rand::thread_rng();
&mut thread_rng
}
};
random(rng);
}
fn random(rng: &mut dyn RngCore) {
println!("{}", rng.gen_range(0..9));
}
Though the more idiomatic way would be to use static dispatch instead, and that does work with Rng
:
fn random(mut rng: impl Rng) {
println!("{}", rng.gen_range(0..9));
}