Consider the case where I have a function make_numbers which should create a string of random numbers, but where I want to decide at runtime (user input) what kind of random number generator should be used. To make it even more difficult, let's assume the make_numbers function to be generic over the type of numbers to be generated.
I wrote what I want to achieve with pseudo code, and I understand why this doesn't work. However, I don't know what an idiomatic way in Rust could look like to achieve this?
My naive ideas would be:
- Use
Box<Rng>, but that doesn't work sinceRnghas generic functions. - Use an enum over
StdRngandXorShiftRng, but I cannot really think of a nice way to write this.
Can you give me some hints as to what a nice solution of this particular problem would look like?
Note: This question is not so much about the different match arms having different types (solutions could be Box or enum, as indicated above) - but how to apply these solutions in this case.
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`
if..elsestatement would be more readable than amatchstatement on abool. The call tomake_numbersalso has to list both type parameters, even if just filled with_to let the compiler infer them:make_numbers::<u8, _>(&mut rng). The implementation ofmake_numberscan become a one-liner with the iterator API:(0..10).map(|_| format!("_{}", rng.gen::<T>())).collect(). Finally, don't forget to callrustfmton your code.Rngtrait isn't object safe, and theRngCoretrait needs to be used instead.