Description
Background
What is your motivation?
rand
offers the Standard
and Uniform
distributions, for making random samples of a primitive or struct. However, to implement them for a new type, the impls (impl Distribution<T> for Standard
for Standard
; impl SampleUniform for T
and impl UniformSampler for TUniformSampler
for Uniform
) must currently be hand-written.
What type of application is this?
No specific type of application, though I write this after having to manually implement Uniform
distribution support for the glam
crate.
Feature request
I propose that derives be added for the Standard
and Uniform
distributions. When used on a struct where all fields already implement support for that type of distribution, it will generate the needed code to use Standard
/Uniform
distributions with that struct.
A derive for the Standard
distribution would probably look like this (click the arrow to expand):
Standard distribution derive
#[derive(StandardRand)]
struct Vec3 {
x: f32,
y: f32,
z: f32,
}
This derive would generate code similar to the following:
impl Distribution<Vec3> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec3 {
Vec3 {
x: rng.gen(),
y: rng.gen(),
z: rng.gen(),
}
}
}
A derive for the Uniform
distribution would be much the same, albeit with the addition of a struct being generated for the UniformSampler
impl:
Uniform distribution derive
#[derive(UniformRand)]
struct Vec3 {
x: f32,
y: f32,
z: f32,
}
generates the following code:
impl SampleUniform for Vec3 {
type Sampler = Vec3UniformSampler;
}
struct Vec3UniformSampler {
x_gen: Uniform<f32>,
y_gen: Uniform<f32>,
z_gen: Uniform<f32>,
}
impl UniformSampler for Vec3UniformSampler {
type X = Vec3;
fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
// Asserts here are technically optional, since our fields are Uniforms,
// but I'm including them anyways for the sake of the example.
assert!(low.x < high.x, "Uniform::new called with `low.x >= high.x");
assert!(low.y < high.y, "Uniform::new called with `low.y >= high.y");
assert!(low.z < high.z, "Uniform::new called with `low.z >= high.z");
Self {
x_gen: Uniform::new(low.x, high.x),
y_gen: Uniform::new(low.y, high.y),
z_gen: Uniform::new(low.z, high.z),
}
}
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(low.x < high.x, "Uniform::new_inclusive called with `low.x >= high.x");
assert!(low.y < high.y, "Uniform::new_inclusive called with `low.y >= high.y");
assert!(low.z < high.z, "Uniform::new_inclusive called with `low.z >= high.z");
Self {
x_gen: Uniform::new_inclusive(low.x, high.x),
y_gen: Uniform::new_inclusive(low.y, high.y),
z_gen: Uniform::new_inclusive(low.z, high.z),
}
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
Self::X {
x: self.x_gen.sample(rng),
y: self.y_gen.sample(rng),
z: self.z_gen.sample(rng),
}
}
// sample_single() and sample_single_inclusive() are not included for the
// sake of shortening the example.
}
If I've missed any details, or if there's any questions you have for me regarding this suggestion, please don't hesitate to let me know.