Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a way to derive Standard and Uniform distributions for a struct #1524

Open
LikeLakers2 opened this issue Nov 6, 2024 · 0 comments
Open

Comments

@LikeLakers2
Copy link

LikeLakers2 commented Nov 6, 2024

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 in this
	// example for the sake of shorting 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant