Skip to content

Commit 585b29f

Browse files
oconnor663dhardy
andauthored
Add top-level helper functions (#1488)
Adds random_iter, random_range, random_bool, random_ratio, fill. See also #989, #1503. Co-authored-by: Diggory Hardy <[email protected]>
1 parent 24b9cc3 commit 585b29f

File tree

3 files changed

+147
-14
lines changed

3 files changed

+147
-14
lines changed

src/lib.rs

Lines changed: 135 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ pub use rng::{Fill, Rng};
121121
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
122122
use crate::distr::{Distribution, Standard};
123123

124-
/// Generates a random value using the thread-local random number generator.
124+
/// Generate a random value using the thread-local random number generator.
125125
///
126-
/// This function is simply a shortcut for `rand::rng().gen()`:
126+
/// This function is shorthand for <code>[rng()].[random()](Rng::random)</code>:
127127
///
128128
/// - See [`ThreadRng`] for documentation of the generator and security
129129
/// - See [`Standard`] for documentation of supported types and distributions
@@ -142,21 +142,15 @@ use crate::distr::{Distribution, Standard};
142142
/// }
143143
/// ```
144144
///
145-
/// If you're calling `random()` in a loop, caching the generator as in the
146-
/// following example can increase performance.
145+
/// If you're calling `random()` repeatedly, consider using a local `rng`
146+
/// handle to save an initialization-check on each usage:
147147
///
148148
/// ```
149-
/// use rand::Rng;
149+
/// use rand::Rng; // provides the `random` method
150150
///
151-
/// let mut v = vec![1, 2, 3];
152-
///
153-
/// for x in v.iter_mut() {
154-
/// *x = rand::random()
155-
/// }
156-
///
157-
/// // can be made faster by caching rand::rng
151+
/// let mut rng = rand::rng(); // a local handle to the generator
158152
///
159-
/// let mut rng = rand::rng();
153+
/// let mut v = vec![1, 2, 3];
160154
///
161155
/// for x in v.iter_mut() {
162156
/// *x = rng.random();
@@ -174,6 +168,127 @@ where
174168
rng().random()
175169
}
176170

171+
/// Return an iterator over [`random()`] variates
172+
///
173+
/// This function is shorthand for
174+
/// <code>[rng()].[random_iter](Rng::random_iter)()</code>.
175+
///
176+
/// # Example
177+
///
178+
/// ```
179+
/// let v: Vec<i32> = rand::random_iter().take(5).collect();
180+
/// println!("{v:?}");
181+
/// ```
182+
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
183+
#[inline]
184+
pub fn random_iter<T>() -> distr::DistIter<Standard, rngs::ThreadRng, T>
185+
where
186+
Standard: Distribution<T>,
187+
{
188+
rng().random_iter()
189+
}
190+
191+
/// Generate a random value in the given range using the thread-local random number generator.
192+
///
193+
/// This function is shorthand for
194+
/// <code>[rng()].[random_range](Rng::random_range)(<var>range</var>)</code>.
195+
///
196+
/// # Example
197+
///
198+
/// ```
199+
/// let y: f32 = rand::random_range(0.0..=1e9);
200+
/// println!("{}", y);
201+
///
202+
/// let words: Vec<&str> = "Mary had a little lamb".split(' ').collect();
203+
/// println!("{}", words[rand::random_range(..words.len())]);
204+
/// ```
205+
/// Note that the first example can also be achieved (without `collect`'ing
206+
/// to a `Vec`) using [`seq::IteratorRandom::choose`].
207+
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
208+
#[inline]
209+
pub fn random_range<T, R>(range: R) -> T
210+
where
211+
T: distr::uniform::SampleUniform,
212+
R: distr::uniform::SampleRange<T>,
213+
{
214+
rng().random_range(range)
215+
}
216+
217+
/// Return a bool with a probability `p` of being true.
218+
///
219+
/// This function is shorthand for
220+
/// <code>[rng()].[random_bool](Rng::random_bool)(<var>p</var>)</code>.
221+
///
222+
/// # Example
223+
///
224+
/// ```
225+
/// println!("{}", rand::random_bool(1.0 / 3.0));
226+
/// ```
227+
///
228+
/// # Panics
229+
///
230+
/// If `p < 0` or `p > 1`.
231+
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
232+
#[inline]
233+
#[track_caller]
234+
pub fn random_bool(p: f64) -> bool {
235+
rng().random_bool(p)
236+
}
237+
238+
/// Return a bool with a probability of `numerator/denominator` of being
239+
/// true.
240+
///
241+
/// That is, `random_ratio(2, 3)` has chance of 2 in 3, or about 67%, of
242+
/// returning true. If `numerator == denominator`, then the returned value
243+
/// is guaranteed to be `true`. If `numerator == 0`, then the returned
244+
/// value is guaranteed to be `false`.
245+
///
246+
/// See also the [`Bernoulli`] distribution, which may be faster if
247+
/// sampling from the same `numerator` and `denominator` repeatedly.
248+
///
249+
/// This function is shorthand for
250+
/// <code>[rng()].[random_ratio](Rng::random_ratio)(<var>numerator</var>, <var>denominator</var>)</code>.
251+
///
252+
/// # Panics
253+
///
254+
/// If `denominator == 0` or `numerator > denominator`.
255+
///
256+
/// # Example
257+
///
258+
/// ```
259+
/// println!("{}", rand::random_ratio(2, 3));
260+
/// ```
261+
///
262+
/// [`Bernoulli`]: distr::Bernoulli
263+
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
264+
#[inline]
265+
#[track_caller]
266+
pub fn random_ratio(numerator: u32, denominator: u32) -> bool {
267+
rng().random_ratio(numerator, denominator)
268+
}
269+
270+
/// Fill any type implementing [`Fill`] with random data
271+
///
272+
/// This function is shorthand for
273+
/// <code>[rng()].[fill](Rng::fill)(<var>dest</var>)</code>.
274+
///
275+
/// # Example
276+
///
277+
/// ```
278+
/// let mut arr = [0i8; 20];
279+
/// rand::fill(&mut arr[..]);
280+
/// ```
281+
///
282+
/// Note that you can instead use [`random()`] to generate an array of random
283+
/// data, though this is slower for small elements (smaller than the RNG word
284+
/// size).
285+
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
286+
#[inline]
287+
#[track_caller]
288+
pub fn fill<T: Fill + ?Sized>(dest: &mut T) {
289+
dest.fill(&mut rng())
290+
}
291+
177292
#[cfg(test)]
178293
mod test {
179294
use super::*;
@@ -200,4 +315,11 @@ mod test {
200315
(f32, (f64, (f64,))),
201316
) = random();
202317
}
318+
319+
#[test]
320+
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
321+
fn test_range() {
322+
let _n: usize = random_range(42..=43);
323+
let _f: f32 = random_range(42.0..43.0);
324+
}
203325
}

src/rng.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,9 @@ pub trait Rng: RngCore {
196196
}
197197

198198
/// Return a bool with a probability of `numerator/denominator` of being
199-
/// true. I.e. `random_ratio(2, 3)` has chance of 2 in 3, or about 67%, of
199+
/// true.
200+
///
201+
/// That is, `random_ratio(2, 3)` has chance of 2 in 3, or about 67%, of
200202
/// returning true. If `numerator == denominator`, then the returned value
201203
/// is guaranteed to be `true`. If `numerator == 0`, then the returned
202204
/// value is guaranteed to be `false`.

src/seq/iterator.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ pub trait IteratorRandom: Iterator + Sized {
5454
/// Consider instead using [`IteratorRandom::choose_stable`] to avoid
5555
/// [`Iterator`] combinators which only change size hints from affecting the
5656
/// results.
57+
///
58+
/// # Example
59+
///
60+
/// ```
61+
/// use rand::seq::IteratorRandom;
62+
///
63+
/// let words = "Mary had a little lamb".split(' ');
64+
/// println!("{}", words.choose(&mut rand::rng()).unwrap());
65+
/// ```
5766
fn choose<R>(mut self, rng: &mut R) -> Option<Self::Item>
5867
where
5968
R: Rng + ?Sized,

0 commit comments

Comments
 (0)