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 rand::range, random_bool, random_iter, random_ratio, fill as top-level helper functions #1488

Merged
merged 10 commits into from
Oct 31, 2024
148 changes: 135 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,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 `rand::rng().gen()`:
/// This function is shorthand for <code>[rng()].[random()](Rng::random)</code>:
///
/// - See [`ThreadRng`] for documentation of the generator and security
/// - See [`Standard`] for documentation of supported types and distributions
Expand All @@ -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()
/// }
///
/// // can be made faster by caching rand::rng
/// let mut rng = rand::rng(); // a local handle to the generator
///
/// let mut rng = rand::rng();
/// let mut v = vec![1, 2, 3];
///
/// for x in v.iter_mut() {
/// *x = rng.random();
Expand All @@ -174,6 +168,127 @@ where
rng().random()
}

/// Return an iterator over [`random()`] variates
///
/// This function is shorthand for
/// <code>[rng()].[random_iter](Rng::random_iter)()</code>.
dhardy marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Example
///
/// ```
/// let v: Vec<i32> = rand::random_iter().take(5).collect();
/// println!("{v:?}");
/// ```
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
#[inline]
pub fn random_iter<T>() -> distr::DistIter<Standard, rngs::ThreadRng, T>
where
Standard: Distribution<T>,
{
rng().random_iter()
}

/// Generate a random value in the given range using the thread-local random number generator.
///
/// This function is shorthand for
/// <code>[rng()].[random_range](Rng::random_range)(<var>range</var>)</code>.
///
/// # Example
///
/// ```
/// let y: f32 = rand::random_range(0.0..=1e9);
/// println!("{}", y);
///
/// 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 random_range<T, R>(range: R) -> T
where
T: distr::uniform::SampleUniform,
R: distr::uniform::SampleRange<T>,
{
rng().random_range(range)
}

/// Return a bool with a probability `p` of being true.
///
/// This function is shorthand for
/// <code>[rng()].[random_bool](Rng::random_bool)(<var>p</var>)</code>.
///
/// # 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
/// <code>[rng()].[random_ratio](Rng::random_ratio)(<var>numerator</var>, <var>denominator</var>)</code>.
///
/// # 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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't quite like this name, but I guess it's better to handle separately.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't much like any of the suggested alternatives, but feel free to open a new PR or re-open #1503 for this.

rng().random_ratio(numerator, denominator)
}

/// Fill any type implementing [`Fill`] with random data
///
/// This function is shorthand for
/// <code>[rng()].[fill](Rng::fill)(<var>dest</var>)</code>.
///
/// # 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<T: Fill + ?Sized>(dest: &mut T) {
dest.fill(&mut rng())
}

#[cfg(test)]
mod test {
use super::*;
Expand All @@ -200,4 +315,11 @@ mod test {
(f32, (f64, (f64,))),
) = random();
}

#[test]
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
fn test_range() {
let _n: usize = random_range(42..=43);
let _f: f32 = random_range(42.0..43.0);
}
}
4 changes: 3 additions & 1 deletion src/rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
9 changes: 9 additions & 0 deletions src/seq/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<R>(mut self, rng: &mut R) -> Option<Self::Item>
where
R: Rng + ?Sized,
Expand Down