From 658b663c309b2a197e9eec84aa5b1a08eaf05af3 Mon Sep 17 00:00:00 2001 From: Kristof Mattei <864376+kristof-mattei@users.noreply.github.com> Date: Fri, 29 May 2026 10:46:47 -0700 Subject: [PATCH] feat: add support for the Rust range types, guarded by a `new_range_api` flag --- Cargo.toml | 8 +++-- src/distr/uniform.rs | 77 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9bbef85456..4a62a66190 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,15 +61,19 @@ unbiased = [] # Deprecated option: enable logging log = [] +new_range_api = [] + [dependencies] rand_core = { version = "0.10.0", default-features = false } serde = { version = "1.0.103", features = ["derive"], optional = true } -chacha20 = { version = "0.10.0", default-features = false, features = ["rng"], optional = true } +chacha20 = { version = "0.10.0", default-features = false, features = [ + "rng", +], optional = true } getrandom = { version = "0.4.0", optional = true } [dev-dependencies] rand_pcg = "0.10" # Only to test serde -postcard = {version = "1.1.3", default-features = false, features = ["alloc"]} +postcard = { version = "1.1.3", default-features = false, features = ["alloc"] } rayon = "1.7" serde_json = "1.0.140" diff --git a/src/distr/uniform.rs b/src/distr/uniform.rs index c61a518e9f..6b4137aded 100644 --- a/src/distr/uniform.rs +++ b/src/distr/uniform.rs @@ -110,7 +110,6 @@ mod other; pub use other::{UniformChar, UniformDuration}; use core::fmt; -use core::ops::{Range, RangeInclusive, RangeTo, RangeToInclusive}; use crate::Rng; use crate::distr::Distribution; @@ -371,15 +370,24 @@ pub trait UniformSampler: Sized { } } -impl TryFrom> for Uniform { +impl TryFrom<::core::ops::Range> for Uniform { type Error = Error; - fn try_from(r: Range) -> Result, Error> { + fn try_from(r: ::core::ops::Range) -> Result, Error> { Uniform::new(r.start, r.end) } } -impl TryFrom> for Uniform { +#[cfg(feature = "new_range_api")] +impl TryFrom<::core::range::Range> for Uniform { + type Error = Error; + + fn try_from(r: ::core::range::Range) -> Result, Error> { + Uniform::new(r.start, r.end) + } +} + +impl TryFrom<::core::ops::RangeInclusive> for Uniform { type Error = Error; fn try_from(r: ::core::ops::RangeInclusive) -> Result, Error> { @@ -387,6 +395,15 @@ impl TryFrom> for Uniform { } } +#[cfg(feature = "new_range_api")] +impl TryFrom<::core::range::RangeInclusive> for Uniform { + type Error = Error; + + fn try_from(r: ::core::range::RangeInclusive) -> Result, Error> { + Uniform::new_inclusive(r.start, r.last) + } +} + /// Helper trait similar to [`Borrow`] but implemented /// only for [`SampleUniform`] and references to [`SampleUniform`] /// in order to resolve ambiguity issues. @@ -429,7 +446,20 @@ pub trait SampleRange { fn is_empty(&self) -> bool; } -impl SampleRange for Range { +impl SampleRange for ::core::ops::Range { + #[inline] + fn sample_single(self, rng: &mut R) -> Result { + T::Sampler::sample_single(self.start, self.end, rng) + } + + #[inline] + fn is_empty(&self) -> bool { + !(self.start < self.end) + } +} + +#[cfg(feature = "new_range_api")] +impl SampleRange for ::core::range::Range { #[inline] fn sample_single(self, rng: &mut R) -> Result { T::Sampler::sample_single(self.start, self.end, rng) @@ -441,7 +471,7 @@ impl SampleRange for Range { } } -impl SampleRange for RangeInclusive { +impl SampleRange for ::core::ops::RangeInclusive { #[inline] fn sample_single(self, rng: &mut R) -> Result { T::Sampler::sample_single_inclusive(self.start(), self.end(), rng) @@ -453,9 +483,22 @@ impl SampleRange for RangeInclusive { } } +#[cfg(feature = "new_range_api")] +impl SampleRange for ::core::range::RangeInclusive { + #[inline] + fn sample_single(self, rng: &mut R) -> Result { + T::Sampler::sample_single_inclusive(self.start, self.last, rng) + } + + #[inline] + fn is_empty(&self) -> bool { + !(self.start <= self.last) + } +} + macro_rules! impl_sample_range_u { ($t:ty) => { - impl SampleRange<$t> for RangeTo<$t> { + impl SampleRange<$t> for ::core::ops::RangeTo<$t> { #[inline] fn sample_single(self, rng: &mut R) -> Result<$t, Error> { <$t as SampleUniform>::Sampler::sample_single(0, self.end, rng) @@ -467,7 +510,7 @@ macro_rules! impl_sample_range_u { } } - impl SampleRange<$t> for RangeToInclusive<$t> { + impl SampleRange<$t> for ::core::ops::RangeToInclusive<$t> { #[inline] fn sample_single(self, rng: &mut R) -> Result<$t, Error> { <$t as SampleUniform>::Sampler::sample_single_inclusive(0, self.end, rng) @@ -478,6 +521,24 @@ macro_rules! impl_sample_range_u { false } } + + // `core::range::RangeTo` is set to be a re-export of `core::ops::RangeTo`: + // > A Rust version in the near future will also add `core::range::RangeFull` and `core::range::RangeTo` + // as re-exports from `core::ops` (these do not implement `Iterator` and already implement `Copy`) + // Source: https://blog.rust-lang.org/2026/05/28/Rust-1.96.0/#new-range-types + + #[cfg(feature = "new_range_api")] + impl SampleRange<$t> for ::core::range::RangeToInclusive<$t> { + #[inline] + fn sample_single(self, rng: &mut R) -> Result<$t, Error> { + <$t as SampleUniform>::Sampler::sample_single_inclusive(0, self.last, rng) + } + + #[inline] + fn is_empty(&self) -> bool { + false + } + } }; }