From 39b79d769aad2b80a159ecc196de48ab6188cf29 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 25 Apr 2026 10:14:32 +0000 Subject: [PATCH 1/3] Add pub trait UniformSamplerRange --- src/distr/uniform.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/distr/uniform.rs b/src/distr/uniform.rs index c61a518e9f..3cd6563f70 100644 --- a/src/distr/uniform.rs +++ b/src/distr/uniform.rs @@ -260,6 +260,23 @@ impl Distribution for Uniform { } } +impl Uniform +where + X::Sampler: UniformSamplerRange, +{ + /// The minimum possible sample value + #[inline] + pub fn min(&self) -> X { + self.0.min() + } + + /// The maximum possible sample value + #[inline] + pub fn max(&self) -> X { + self.0.max() + } +} + /// Helper trait for creating objects using the correct implementation of /// [`UniformSampler`] for the sampling type. /// @@ -371,6 +388,15 @@ pub trait UniformSampler: Sized { } } +/// Extension trait providing min/max sampling range +pub trait UniformSamplerRange: UniformSampler { + /// The minimum possible sample value + fn min(&self) -> Self::X; + + /// The maximum possible sample value + fn max(&self) -> Self::X; +} + impl TryFrom> for Uniform { type Error = Error; From 784c3bc74b441c5025c9498097b49d460944cb46 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 25 Apr 2026 10:14:53 +0000 Subject: [PATCH 2/3] impl UniformSamplerRange for UniformInt samplers --- src/distr/uniform_int.rs | 87 +++++++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/src/distr/uniform_int.rs b/src/distr/uniform_int.rs index 865d63f69d..7749139a24 100644 --- a/src/distr/uniform_int.rs +++ b/src/distr/uniform_int.rs @@ -9,7 +9,7 @@ //! `UniformInt` implementation -use super::{Error, SampleBorrow, SampleUniform, UniformSampler}; +use super::{Error, SampleBorrow, SampleUniform, UniformSampler, UniformSamplerRange}; use crate::distr::utils::WideningMultiply; #[cfg(feature = "simd_support")] use crate::distr::{Distribution, StandardUniform}; @@ -255,6 +255,18 @@ macro_rules! uniform_int_impl { Ok(low.wrapping_add(result as $ty)) } } + + impl UniformSamplerRange for UniformInt<$ty> { + #[inline] + fn min(&self) -> $ty { + self.low + } + + #[inline] + fn max(&self) -> $ty { + self.low.wrapping_add(self.range.wrapping_sub(1)) + } + } }; } @@ -372,6 +384,23 @@ macro_rules! uniform_simd_int_impl { } } } + + impl UniformSamplerRange for UniformInt> + where + Simd<$unsigned, LANES>: + WideningMultiply, Simd<$unsigned, LANES>)>, + StandardUniform: Distribution>, + { + #[inline] + fn min(&self) -> Self::X { + self.low + } + + #[inline] + fn max(&self) -> Self::X { + self.low + self.range - Simd::splat(1) + } + } }; // bulk implementation @@ -580,6 +609,18 @@ impl UniformSampler for UniformUsize { } } +impl UniformSamplerRange for UniformUsize { + #[inline] + fn min(&self) -> usize { + self.low + } + + #[inline] + fn max(&self) -> usize { + self.low.wrapping_add(self.range.wrapping_sub(1)) + } +} + #[cfg(test)] mod tests { use super::*; @@ -596,6 +637,8 @@ mod tests { fn test_uniform_good_limits_equal_int() { let mut rng = crate::test::rng(804); let dist = Uniform::new_inclusive(10, 10).unwrap(); + assert_eq!(dist.min(), 10); + assert_eq!(dist.max(), 10); for _ in 0..20 { assert_eq!(rng.sample(dist), 10); } @@ -614,24 +657,36 @@ mod tests { ($ty:ident, $v:expr, $le:expr, $lt:expr) => {{ for &(low, high) in $v.iter() { let my_uniform = Uniform::new(low, high).unwrap(); + assert_eq!(my_uniform.min(), low); + // assert_eq!(my_uniform.max(), high - 1); + for _ in 0..1000 { let v: $ty = rng.sample(my_uniform); assert!($le(low, v) && $lt(v, high)); } let my_uniform = Uniform::new_inclusive(low, high).unwrap(); + assert_eq!(my_uniform.min(), low); + assert_eq!(my_uniform.max(), high); + for _ in 0..1000 { let v: $ty = rng.sample(my_uniform); assert!($le(low, v) && $le(v, high)); } let my_uniform = Uniform::new(&low, high).unwrap(); + assert_eq!(my_uniform.min(), low); + // assert_eq!(my_uniform.max(), high - 1); + for _ in 0..1000 { let v: $ty = rng.sample(my_uniform); assert!($le(low, v) && $lt(v, high)); } let my_uniform = Uniform::new_inclusive(&low, &high).unwrap(); + assert_eq!(my_uniform.min(), low); + assert_eq!(my_uniform.max(), high); + for _ in 0..1000 { let v: $ty = rng.sample(my_uniform); assert!($le(low, v) && $le(v, high)); @@ -691,8 +746,8 @@ mod tests { #[test] fn test_uniform_from_std_range() { let r = Uniform::try_from(2u32..7).unwrap(); - assert_eq!(r.0.low, 2); - assert_eq!(r.0.range, 5); + assert_eq!(r.min(), 2); + assert_eq!(r.max(), 6); } #[test] @@ -705,8 +760,8 @@ mod tests { #[test] fn test_uniform_from_std_range_inclusive() { let r = Uniform::try_from(2u32..=6).unwrap(); - assert_eq!(r.0.low, 2); - assert_eq!(r.0.range, 5); + assert_eq!(r.min(), 2); + assert_eq!(r.max(), 6); } #[test] @@ -718,13 +773,11 @@ mod tests { #[test] fn value_stability() { - fn test_samples>( - lb: T, - ub: T, - ub_excl: T, - expected: &[T], - ) where + fn test_samples(lb: T, ub: T, ub_excl: T, expected: &[T]) + where Uniform: Distribution, + T: SampleUniform + Copy + Debug + PartialEq + Add, + T::Sampler: UniformSamplerRange, { let mut rng = crate::test::rng(897); let mut buf = [lb; 6]; @@ -734,6 +787,9 @@ mod tests { } let distr = Uniform::new_inclusive(lb, ub).unwrap(); + assert_eq!(distr.min(), lb); + assert_eq!(distr.max(), ub); + for x in &mut buf[3..6] { *x = rng.sample(&distr); } @@ -746,6 +802,9 @@ mod tests { } let distr = Uniform::new(lb, ub_excl).unwrap(); + assert_eq!(distr.min(), lb); + assert_eq!(distr.max(), ub); + for x in &mut buf[3..6] { *x = rng.sample(&distr); } @@ -866,6 +925,9 @@ mod tests { fn test_uniform_usize_deserialization() { use serde_json; let original = UniformUsize::new_inclusive(10, 100).expect("creation"); + assert_eq!(original.min(), 10); + assert_eq!(original.max(), 100); + let serialized = serde_json::to_string(&original).expect("serialization"); let deserialized: UniformUsize = serde_json::from_str(&serialized).expect("deserialization"); @@ -890,7 +952,10 @@ mod tests { fn test_uniform_usize_deserialization_64bit() { use serde_json; let original = UniformUsize::new_inclusive(1, u64::MAX as usize - 1).expect("creation"); + assert_eq!(original.min(), 1); + assert_eq!(original.max(), u64::MAX as usize - 1); assert!(original.mode64); + let serialized = serde_json::to_string(&original).expect("serialization"); let deserialized: UniformUsize = serde_json::from_str(&serialized).expect("deserialization"); From 0eba3e671a0a9ed14aa050704b8c67617179293a Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 25 Apr 2026 11:13:44 +0000 Subject: [PATCH 3/3] CHANGELOG entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e0e189f60..cc46771140 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,11 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update. ### Changes - Document required output order of fn `partial_shuffle` and apply `#[must_use]` ([#1769]) +### Additions +- Add trait `UniformSamplerRange` and fns `Uniform::{min, max}` ([#1775]) + [#1769]: https://github.com/rust-random/rand/pull/1769 +[#1775]: https://github.com/rust-random/rand/pull/1775 ## [0.10.1] — 2026-02-11 This release includes a fix for a soundness bug; see [#1763].