From 4e52512587d6a951404e511d085fe368308cf434 Mon Sep 17 00:00:00 2001 From: Jack O'Connor Date: Wed, 14 Aug 2024 09:04:49 -0700 Subject: [PATCH 1/9] Add the `rand::range` top-level function --- src/lib.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ce0206db09..d77f89ef4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ //! # Quick Start //! //! To get you started quickly, the easiest and highest-level way to get -//! a random value is to use [`random()`]; alternatively you can use +//! a random value is to use [`random()`] or [`range()`]; alternatively you can use //! [`thread_rng()`]. The [`Rng`] trait provides a useful API on all RNGs, while //! the [`distr`] and [`seq`] modules provide further //! functionality on top of RNGs. @@ -111,9 +111,9 @@ pub use rng::{Fill, Rng}; #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] use crate::distr::{Distribution, Standard}; -/// Generates a random value using the thread-local random number generator. +/// Generate a random value using the thread-local random number generator. /// -/// This function is simply a shortcut for `thread_rng().gen()`: +/// This function is a shortcut for [`thread_rng().random()`](Rng::random): /// /// - See [`ThreadRng`] for documentation of the generator and security /// - See [`Standard`] for documentation of supported types and distributions @@ -164,6 +164,50 @@ where thread_rng().random() } +/// Generate a random value in the given range using the thread-local random number generator. +/// +/// This function is a shortcut for [`thread_rng().gen_range(range)`](Rng::gen_range). +/// +/// # Examples +/// +/// ``` +/// let x = rand::range(1u8..=100); +/// println!("{}", x); +/// +/// let y = rand::range(0f32..=1e9); +/// println!("{}", y); +/// ``` +/// +/// If you're calling `range()` in a loop, caching the generator as in the +/// following example can increase performance. +/// +/// ``` +/// use rand::Rng; +/// +/// let mut v = vec![1, 2, 3]; +/// +/// for x in v.iter_mut() { +/// *x = rand::range(1..=3) +/// } +/// +/// // can be made faster by caching thread_rng +/// +/// let mut rng = rand::thread_rng(); +/// +/// for x in v.iter_mut() { +/// *x = rng.gen_range(1..=3) +/// } +/// ``` +#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] +#[inline] +pub fn range(range: R) -> T +where + T: distr::uniform::SampleUniform, + R: distr::uniform::SampleRange, +{ + thread_rng().gen_range(range) +} + #[cfg(test)] mod test { use super::*; @@ -190,4 +234,11 @@ mod test { (f32, (f64, (f64,))), ) = random(); } + + #[test] + #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] + fn test_range() { + let _n: usize = range(42..=43); + let _f: f32 = range(42.0..43.0); + } } From 2aa33fae183d9df786645eeadbd78d6829eda39a Mon Sep 17 00:00:00 2001 From: Jack O'Connor Date: Wed, 14 Aug 2024 09:39:11 -0700 Subject: [PATCH 2/9] Add the `rand::shuffle` top-level function --- src/lib.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index d77f89ef4f..67938a8445 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -208,6 +208,27 @@ where thread_rng().gen_range(range) } +/// Shuffle a mutable slice in place using the thread-local random number generator. +/// +/// This function is a shortcut for [`slice.shuffle(&mut thread_rng())`](seq::SliceRandom::shuffle): +/// +/// For slices of length `n`, complexity is `O(n)`. +/// The resulting permutation is picked uniformly from the set of all possible permutations. +/// +/// # Example +/// +/// ``` +/// let mut y = [1, 2, 3, 4, 5]; +/// println!("Unshuffled: {:?}", y); +/// rand::shuffle(&mut y); +/// println!("Shuffled: {:?}", y); +/// ``` +#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] +#[inline] +pub fn shuffle(slice: &mut [T]) { + seq::SliceRandom::shuffle(slice, &mut thread_rng()); +} + #[cfg(test)] mod test { use super::*; @@ -241,4 +262,22 @@ mod test { let _n: usize = range(42..=43); let _f: f32 = range(42.0..43.0); } + + #[test] + #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] + fn test_shuffle() { + let mut array1 = [0; 100]; + for i in 0..array1.len() { + array1[i] = i; + } + let mut array2 = array1; + assert_eq!(array1, array2); + shuffle(&mut array1); + assert_ne!(array1, array2); // practically impossible without an RNG bug + shuffle(&mut array2); + assert_ne!(array1, array2); // same + array1.sort(); + array2.sort(); + assert_eq!(array1, array2); + } } From 681a893d85c5efd90f2d27cc96ebab550f0c0121 Mon Sep 17 00:00:00 2001 From: Jack O'Connor Date: Wed, 14 Aug 2024 13:14:42 -0700 Subject: [PATCH 3/9] clippy fix --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 67938a8445..5c5b3c8a6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -267,8 +267,8 @@ mod test { #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] fn test_shuffle() { let mut array1 = [0; 100]; - for i in 0..array1.len() { - array1[i] = i; + for (i, x) in array1.iter_mut().enumerate() { + *x = i; } let mut array2 = array1; assert_eq!(array1, array2); From c8222cf2e0b526c52e9a1189d3c30b401e925b9f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 23 Oct 2024 11:25:52 +0100 Subject: [PATCH 4/9] =?UTF-8?q?thread=5Frng=20=E2=86=92=20rng,=20doc=20cor?= =?UTF-8?q?rection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 03afa65bd6..c231f68067 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,7 +123,7 @@ use crate::distr::{Distribution, Standard}; /// Generate a random value using the thread-local random number generator. /// -/// This function is a shortcut for [`thread_rng().random()`](Rng::random): +/// This function is a shortcut for [`rng().random()`](Rng::random): /// /// - See [`ThreadRng`] for documentation of the generator and security /// - See [`Standard`] for documentation of supported types and distributions @@ -176,7 +176,7 @@ where /// Generate a random value in the given range using the thread-local random number generator. /// -/// This function is a shortcut for [`thread_rng().gen_range(range)`](Rng::gen_range). +/// This function is a shortcut for [`rng().gen_range(range)`](Rng::gen_range). /// /// # Examples /// @@ -200,12 +200,14 @@ where /// *x = rand::range(1..=3) /// } /// -/// // can be made faster by caching thread_rng +/// // The above can be made slightly faster by caching [`rand::rng()`](crate::rng) +/// // and using a [`rand::distr::Uniform`](crate::distr::Uniform) distribution: /// -/// let mut rng = rand::thread_rng(); +/// let mut rng = rand::rng(); +/// let distribution = rand::distr::Uniform::try_from(1..=3).unwrap(); /// /// for x in v.iter_mut() { -/// *x = rng.gen_range(1..=3) +/// *x = rng.sample(distribution); /// } /// ``` #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] @@ -215,12 +217,12 @@ where T: distr::uniform::SampleUniform, R: distr::uniform::SampleRange, { - thread_rng().gen_range(range) + rng().random_range(range) } /// Shuffle a mutable slice in place using the thread-local random number generator. /// -/// This function is a shortcut for [`slice.shuffle(&mut thread_rng())`](seq::SliceRandom::shuffle): +/// This function is a shortcut for [`slice.shuffle(&mut rng())`](seq::SliceRandom::shuffle): /// /// For slices of length `n`, complexity is `O(n)`. /// The resulting permutation is picked uniformly from the set of all possible permutations. @@ -236,7 +238,7 @@ where #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] #[inline] pub fn shuffle(slice: &mut [T]) { - seq::SliceRandom::shuffle(slice, &mut thread_rng()); + seq::SliceRandom::shuffle(slice, &mut rng()); } #[cfg(test)] From e4512effc6d4ccf60aad11e411ce6c4a9864d9a3 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 23 Oct 2024 11:27:25 +0100 Subject: [PATCH 5/9] Remove rand::shuffle --- src/lib.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c231f68067..28feb94862 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -220,27 +220,6 @@ where rng().random_range(range) } -/// Shuffle a mutable slice in place using the thread-local random number generator. -/// -/// This function is a shortcut for [`slice.shuffle(&mut rng())`](seq::SliceRandom::shuffle): -/// -/// For slices of length `n`, complexity is `O(n)`. -/// The resulting permutation is picked uniformly from the set of all possible permutations. -/// -/// # Example -/// -/// ``` -/// let mut y = [1, 2, 3, 4, 5]; -/// println!("Unshuffled: {:?}", y); -/// rand::shuffle(&mut y); -/// println!("Shuffled: {:?}", y); -/// ``` -#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] -#[inline] -pub fn shuffle(slice: &mut [T]) { - seq::SliceRandom::shuffle(slice, &mut rng()); -} - #[cfg(test)] mod test { use super::*; From e8bc332e3679e52f98919e3ad9314d3949d2c97b Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 23 Oct 2024 12:02:02 +0100 Subject: [PATCH 6/9] Revise doc for rand::random, random_range --- src/lib.rs | 78 ++++++++++----------------------------------- src/seq/iterator.rs | 9 ++++++ 2 files changed, 26 insertions(+), 61 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 28feb94862..49e561a23a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,7 +123,7 @@ use crate::distr::{Distribution, Standard}; /// Generate a random value using the thread-local random number generator. /// -/// This function is a shortcut for [`rng().random()`](Rng::random): +/// This function is shorthand for [rng()].[random()](Rng::random): /// /// - See [`ThreadRng`] for documentation of the generator and security /// - See [`Standard`] for documentation of supported types and distributions @@ -142,21 +142,15 @@ use crate::distr::{Distribution, Standard}; /// } /// ``` /// -/// If you're calling `random()` in a loop, caching the generator as in the -/// following example can increase performance. +/// If you're calling `random()` repeatedly, consider using a local `rng` +/// handle to save an initialization-check on each usage: /// /// ``` -/// use rand::Rng; +/// use rand::Rng; // provides the `random` method /// -/// let mut v = vec![1, 2, 3]; -/// -/// for x in v.iter_mut() { -/// *x = rand::random() -/// } +/// let mut rng = rand::rng(); // a local handle to the generator /// -/// // can be made faster by caching rand::rng -/// -/// let mut rng = rand::rng(); +/// let mut v = vec![1, 2, 3]; /// /// for x in v.iter_mut() { /// *x = rng.random(); @@ -176,43 +170,23 @@ where /// Generate a random value in the given range using the thread-local random number generator. /// -/// This function is a shortcut for [`rng().gen_range(range)`](Rng::gen_range). +/// This function is shorthand for +/// [rng()].[random_range](Rng::random_range)(range). /// -/// # Examples +/// # Example /// /// ``` -/// let x = rand::range(1u8..=100); -/// println!("{}", x); -/// -/// let y = rand::range(0f32..=1e9); +/// let y: f32 = rand::random_range(0.0..=1e9); /// println!("{}", y); -/// ``` -/// -/// If you're calling `range()` in a loop, caching the generator as in the -/// following example can increase performance. -/// -/// ``` -/// use rand::Rng; /// -/// let mut v = vec![1, 2, 3]; -/// -/// for x in v.iter_mut() { -/// *x = rand::range(1..=3) -/// } -/// -/// // The above can be made slightly faster by caching [`rand::rng()`](crate::rng) -/// // and using a [`rand::distr::Uniform`](crate::distr::Uniform) distribution: -/// -/// let mut rng = rand::rng(); -/// let distribution = rand::distr::Uniform::try_from(1..=3).unwrap(); -/// -/// for x in v.iter_mut() { -/// *x = rng.sample(distribution); -/// } +/// let words: Vec<&str> = "Mary had a little lamb".split(' ').collect(); +/// println!("{}", words[rand::random_range(..words.len())]); /// ``` +/// Note that the first example can also be achieved (without `collect`'ing +/// to a `Vec`) using [`seq::IteratorRandom::choose`]. #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] #[inline] -pub fn range(range: R) -> T +pub fn random_range(range: R) -> T where T: distr::uniform::SampleUniform, R: distr::uniform::SampleRange, @@ -250,25 +224,7 @@ mod test { #[test] #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] fn test_range() { - let _n: usize = range(42..=43); - let _f: f32 = range(42.0..43.0); - } - - #[test] - #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] - fn test_shuffle() { - let mut array1 = [0; 100]; - for (i, x) in array1.iter_mut().enumerate() { - *x = i; - } - let mut array2 = array1; - assert_eq!(array1, array2); - shuffle(&mut array1); - assert_ne!(array1, array2); // practically impossible without an RNG bug - shuffle(&mut array2); - assert_ne!(array1, array2); // same - array1.sort(); - array2.sort(); - assert_eq!(array1, array2); + let _n: usize = random_range(42..=43); + let _f: f32 = random_range(42.0..43.0); } } diff --git a/src/seq/iterator.rs b/src/seq/iterator.rs index ad96b3baf7..b10d205676 100644 --- a/src/seq/iterator.rs +++ b/src/seq/iterator.rs @@ -54,6 +54,15 @@ pub trait IteratorRandom: Iterator + Sized { /// Consider instead using [`IteratorRandom::choose_stable`] to avoid /// [`Iterator`] combinators which only change size hints from affecting the /// results. + /// + /// # Example + /// + /// ``` + /// use rand::seq::IteratorRandom; + /// + /// let words = "Mary had a little lamb".split(' '); + /// println!("{}", words.choose(&mut rand::rng()).unwrap()); + /// ``` fn choose(mut self, rng: &mut R) -> Option where R: Rng + ?Sized, From a864f6c86dd2dbeba81afcd59a406a0dca41352f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 23 Oct 2024 12:11:49 +0100 Subject: [PATCH 7/9] Add rand::random_iter --- src/lib.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 49e561a23a..ab7b7d8ae1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -168,6 +168,26 @@ where rng().random() } +/// Return an iterator over [`random()`] variates +/// +/// This function is shorthand for +/// [rng()].[random_iter](Rng::random_iter)(). +/// +/// # Example +/// +/// ``` +/// let v: Vec = rand::random_iter().take(5).collect(); +/// println!("{v:?}"); +/// ``` +#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] +#[inline] +pub fn random_iter() -> distr::DistIter +where + Standard: Distribution, +{ + rng().random_iter() +} + /// Generate a random value in the given range using the thread-local random number generator. /// /// This function is shorthand for From 0eb5ba99d60eb75105086d79b1977c3e309abf86 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 23 Oct 2024 12:16:01 +0100 Subject: [PATCH 8/9] Add rand::random_bool, random_ratio --- src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rng.rs | 4 +++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ab7b7d8ae1..2e6b356530 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -214,6 +214,59 @@ where rng().random_range(range) } +/// Return a bool with a probability `p` of being true. +/// +/// This function is shorthand for +/// [rng()].[random_bool](Rng::random_bool)(p). +/// +/// # Example +/// +/// ``` +/// println!("{}", rand::random_bool(1.0 / 3.0)); +/// ``` +/// +/// # Panics +/// +/// If `p < 0` or `p > 1`. +#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] +#[inline] +#[track_caller] +pub fn random_bool(p: f64) -> bool { + rng().random_bool(p) +} + +/// Return a bool with a probability of `numerator/denominator` of being +/// true. +/// +/// That is, `random_ratio(2, 3)` has chance of 2 in 3, or about 67%, of +/// returning true. If `numerator == denominator`, then the returned value +/// is guaranteed to be `true`. If `numerator == 0`, then the returned +/// value is guaranteed to be `false`. +/// +/// See also the [`Bernoulli`] distribution, which may be faster if +/// sampling from the same `numerator` and `denominator` repeatedly. +/// +/// This function is shorthand for +/// [rng()].[random_ratio](Rng::random_ratio)(numerator, denominator). +/// +/// # Panics +/// +/// If `denominator == 0` or `numerator > denominator`. +/// +/// # Example +/// +/// ``` +/// println!("{}", rand::random_ratio(2, 3)); +/// ``` +/// +/// [`Bernoulli`]: distr::Bernoulli +#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] +#[inline] +#[track_caller] +pub fn random_ratio(numerator: u32, denominator: u32) -> bool { + rng().random_ratio(numerator, denominator) +} + #[cfg(test)] mod test { use super::*; diff --git a/src/rng.rs b/src/rng.rs index a3657ed45f..9ac481ed9c 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -196,7 +196,9 @@ pub trait Rng: RngCore { } /// Return a bool with a probability of `numerator/denominator` of being - /// true. I.e. `random_ratio(2, 3)` has chance of 2 in 3, or about 67%, of + /// true. + /// + /// That is, `random_ratio(2, 3)` has chance of 2 in 3, or about 67%, of /// returning true. If `numerator == denominator`, then the returned value /// is guaranteed to be `true`. If `numerator == 0`, then the returned /// value is guaranteed to be `false`. From 1a1d84893f41afbceff014c91316702430312208 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 23 Oct 2024 12:19:12 +0100 Subject: [PATCH 9/9] Add rand::fill --- src/lib.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 2e6b356530..f909292547 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -267,6 +267,28 @@ pub fn random_ratio(numerator: u32, denominator: u32) -> bool { rng().random_ratio(numerator, denominator) } +/// Fill any type implementing [`Fill`] with random data +/// +/// This function is shorthand for +/// [rng()].[fill](Rng::fill)(dest). +/// +/// # Example +/// +/// ``` +/// let mut arr = [0i8; 20]; +/// rand::fill(&mut arr[..]); +/// ``` +/// +/// Note that you can instead use [`random()`] to generate an array of random +/// data, though this is slower for small elements (smaller than the RNG word +/// size). +#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] +#[inline] +#[track_caller] +pub fn fill(dest: &mut T) { + dest.fill(&mut rng()) +} + #[cfg(test)] mod test { use super::*;