Hi,
I have a project where I need to generate random values multiple times and in different sections (different functions) in the code using rand crate. So, is it better to make "ThreadRng" a member of the struct and use it when necessary using "self" like the following code, or for each function I should define new "ThreadRng" by declaring "let mut rng = rand::thread_rng();" ??
use rand::distributions::{Distribution, Uniform};
struct MyStruct {
rng : ThreadRng,
}
impl MyStruct {
// first function uses rng
fn f1(&mut self)->Vec<f64>{
let _range = Uniform::from(0.0f64..=1.0f64);
// or for each function I hould define rng using the following instruction ?
// let mut rng = rand::thread_rng();
let mut rand_vec : Vec<f64> = Vec::with_capacity(10);
for i in 0..10 {
rand_vec.push(_range.sample(&mut self.rng));
}
rand_vec
}
// second function uses rng
fn f2(&mut self)->Vec<f64>{
let _range = Uniform::from(-100.0f64..=100.0f64);
// or for each function I hould define rng using the following instruction ?
// let mut rng = rand::thread_rng();
let mut rand_vec : Vec<f64> = Vec::with_capacity(10);
for i in 0..10 {
rand_vec.push(_range.sample(&mut self.rng));
}
rand_vec
}
}
rand::thread_rng() already caches the ThreadRng object after the first call and retrieves it on later calls, so storing it yourself makes no difference.
I am not sure if this is the best... But, in my Rendering lib, I am using SmallRng (I think it is not good for cryptographic applications) and keeping it independently. The reason I do this is because:
I need several of them—one per thread—and they cannot be correlated (using the same one created weird artefacts)
I found that storing them in MyStruct can be painful because that means that any method that produces a random number needs to use &mut self, which I find kind nonsense (unsafe?).
Let me explain the second point:
// Doing this
struct Material {
reflectivity: f64,
rng: SmallRng
}
// Implies that this method receives a mutable reference to Self,
// EVEN IF THE PROPERTIES OF THE MATERIAL DO NOT CHANGE
impl Material{
fn reflect(&mut self, ray_in: &Ray)->{
// .... this is not actual code.
let (new_x, new_y, new_z) : (f64, f64, f64) = self.rng.gen();
// return a random vector
Vector3D::new(new_x, new_y, new_z)
}
}
On the contrary:
// Doing this
struct Material {
reflectivity: f64
}
// Means that I can call the same method with an immutable reference to Self.
// THIS ALSO MEANS THAT I CAN PASS THE MATERIAL OBJECT TO DIFFERENT
// THREADS SAFELY!
impl Material {
fn reflect(&self, ray_in: &Ray, rng: &mut SmallRng)->{
// .... this is not actual code.
let (new_x, new_y, new_z) : (f64, f64, f64) = rng.gen();
// return a random vector
Vector3D::new(new_x, new_y, new_z)
}
}
This is my view, at least.... I guess it will really depend on the kind of application you targeting. If you were coding a set of gambling machines, or a set of random-walking entities, maybe it makes sense to make them mutable (e.g., fn walk(&mut self){ self.position += self.rng.gen(); }).