From 576efe25dce4920f8374832d74057bf9a6f9eb6b 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 1/2] feat: add support for the Rust range types, guarded by a `new_range_api` flag --- Cargo.toml | 2 ++ src/distr/uniform.rs | 77 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9bbef85456..19edbf0d6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,8 @@ 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 } 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 + } + } }; } From e13bba79f853b980877245f015669ef415376502 Mon Sep 17 00:00:00 2001 From: Kristof Mattei <864376+kristof-mattei@users.noreply.github.com> Date: Sat, 30 May 2026 09:58:36 -0700 Subject: [PATCH 2/2] fix: rename feature to indicate rustc version requirements --- Cargo.toml | 3 ++- src/distr/uniform.rs | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 19edbf0d6c..80951a3f3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,8 @@ unbiased = [] # Deprecated option: enable logging log = [] -new_range_api = [] +# Support the std::range types stabilised in Rust 1.96.0. +rust_1_96 = [] [dependencies] rand_core = { version = "0.10.0", default-features = false } diff --git a/src/distr/uniform.rs b/src/distr/uniform.rs index 6b4137aded..99dd33b33d 100644 --- a/src/distr/uniform.rs +++ b/src/distr/uniform.rs @@ -378,7 +378,7 @@ impl TryFrom<::core::ops::Range> for Uniform { } } -#[cfg(feature = "new_range_api")] +#[cfg(feature = "rust_1_96")] impl TryFrom<::core::range::Range> for Uniform { type Error = Error; @@ -395,7 +395,7 @@ impl TryFrom<::core::ops::RangeInclusive> for Uniform { } } -#[cfg(feature = "new_range_api")] +#[cfg(feature = "rust_1_96")] impl TryFrom<::core::range::RangeInclusive> for Uniform { type Error = Error; @@ -458,7 +458,7 @@ impl SampleRange for ::core::ops::Range { } } -#[cfg(feature = "new_range_api")] +#[cfg(feature = "rust_1_96")] impl SampleRange for ::core::range::Range { #[inline] fn sample_single(self, rng: &mut R) -> Result { @@ -483,7 +483,7 @@ impl SampleRange for ::core::ops::RangeInclusi } } -#[cfg(feature = "new_range_api")] +#[cfg(feature = "rust_1_96")] impl SampleRange for ::core::range::RangeInclusive { #[inline] fn sample_single(self, rng: &mut R) -> Result { @@ -527,7 +527,7 @@ macro_rules! impl_sample_range_u { // 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")] + #[cfg(feature = "rust_1_96")] impl SampleRange<$t> for ::core::range::RangeToInclusive<$t> { #[inline] fn sample_single(self, rng: &mut R) -> Result<$t, Error> {