Skip to content

Commit

Permalink
Doc: improve random, thread_rng, ThreadRng docs (#1257)
Browse files Browse the repository at this point in the history
* Use a custom Debug impl for ThreadRng

* Adjust documentation of random, thread_rng, ThreadRng

* Fix no-std build

* Compatibility with older rustc
  • Loading branch information
dhardy committed Sep 30, 2022
1 parent 2b4f00a commit 766c7ec
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 49 deletions.
5 changes: 3 additions & 2 deletions rand_core/src/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ use getrandom::getrandom;
/// The implementation is provided by the [getrandom] crate. Refer to
/// [getrandom] documentation for details.
///
/// This struct is only available when specifying the crate feature `getrandom`
/// or `std`. When using the `rand` lib, it is also available as `rand::rngs::OsRng`.
/// This struct is available as `rand_core::OsRng` and as `rand::rngs::OsRng`.
/// In both cases, this requires the crate feature `getrandom` or `std`
/// (enabled by default in `rand` but not in `rand_core`).
///
/// # Blocking and error handling
///
Expand Down
33 changes: 4 additions & 29 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,36 +110,10 @@ use crate::distributions::{Distribution, Standard};

/// Generates a random value using the thread-local random number generator.
///
/// This is simply a shortcut for `thread_rng().gen()`. See [`thread_rng`] for
/// documentation of the entropy source and [`Standard`] for documentation of
/// distributions and type-specific generation.
/// This function is simply a shortcut for `thread_rng().gen()`:
///
/// # Provided implementations
///
/// The following types have provided implementations that
/// generate values with the following ranges and distributions:
///
/// * Integers (`i32`, `u32`, `isize`, `usize`, etc.): Uniformly distributed
/// over all values of the type.
/// * `char`: Uniformly distributed over all Unicode scalar values, i.e. all
/// code points in the range `0...0x10_FFFF`, except for the range
/// `0xD800...0xDFFF` (the surrogate code points). This includes
/// unassigned/reserved code points.
/// * `bool`: Generates `false` or `true`, each with probability 0.5.
/// * Floating point types (`f32` and `f64`): Uniformly distributed in the
/// half-open range `[0, 1)`. See notes below.
/// * Wrapping integers (`Wrapping<T>`), besides the type identical to their
/// normal integer variants.
///
/// Also supported is the generation of the following
/// compound types where all component types are supported:
///
/// * Tuples (up to 12 elements): each element is generated sequentially.
/// * Arrays (up to 32 elements): each element is generated sequentially;
/// see also [`Rng::fill`] which supports arbitrary array length for integer
/// types and tends to be faster for `u32` and smaller types.
/// * `Option<T>` first generates a `bool`, and if true generates and returns
/// `Some(value)` where `value: T`, otherwise returning `None`.
/// - See [`ThreadRng`] for documentation of the generator and security
/// - See [`Standard`] for documentation of supported types and distributions
///
/// # Examples
///
Expand Down Expand Up @@ -177,6 +151,7 @@ use crate::distributions::{Distribution, Standard};
/// ```
///
/// [`Standard`]: distributions::Standard
/// [`ThreadRng`]: rngs::ThreadRng
#[cfg(all(feature = "std", feature = "std_rng"))]
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng"))))]
#[inline]
Expand Down
2 changes: 1 addition & 1 deletion src/rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use core::{mem, slice};
/// # let v = foo(&mut thread_rng());
/// ```
pub trait Rng: RngCore {
/// Return a random value supporting the [`Standard`] distribution.
/// Return a random value via the [`Standard`] distribution.
///
/// # Example
///
Expand Down
64 changes: 47 additions & 17 deletions src/rngs/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use core::cell::UnsafeCell;
use std::rc::Rc;
use std::thread_local;
use std::fmt;

use super::std::Core;
use crate::rngs::adapter::ReseedingRng;
Expand Down Expand Up @@ -39,31 +40,43 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;

/// A reference to the thread-local generator
///
/// This type is a reference to a lazily-initialized thread-local generator.
/// An instance can be obtained via [`thread_rng`] or via `ThreadRng::default()`.
/// This handle is safe to use everywhere (including thread-local destructors),
/// though it is recommended not to use inside a fork handler.
/// The handle cannot be passed between threads (is not `Send` or `Sync`).
///
/// `ThreadRng` uses the same PRNG as [`StdRng`] for security and performance
/// and is automatically seeded from [`OsRng`].
/// `ThreadRng` uses the same CSPRNG as [`StdRng`], ChaCha12. As with
/// [`StdRng`], the algorithm may be changed, subject to reasonable expectations
/// of security and performance.
///
/// Unlike `StdRng`, `ThreadRng` uses the [`ReseedingRng`] wrapper to reseed
/// the PRNG from fresh entropy every 64 kiB of random data as well as after a
/// fork on Unix (though not quite immediately; see documentation of
/// [`ReseedingRng`]).
/// Note that the reseeding is done as an extra precaution against side-channel
/// attacks and mis-use (e.g. if somehow weak entropy were supplied initially).
/// The PRNG algorithms used are assumed to be secure.
/// `ThreadRng` is automatically seeded from [`OsRng`] with periodic reseeding
/// (every 64 kiB, as well as "soon" after a fork on Unix — see [`ReseedingRng`]
/// documentation for details).
///
/// Security must be considered relative to a thread model and validation
/// requirements. `ThreadRng` attempts to meet basic security considerations
/// for producing unpredictable random numbers: use a CSPRNG, use a
/// recommended platform-specific seed ([`OsRng`]), and avoid
/// leaking internal secrets e.g. via [`Debug`] implementation or serialization.
/// Memory is not zeroized on drop.
///
/// [`ReseedingRng`]: crate::rngs::adapter::ReseedingRng
/// [`StdRng`]: crate::rngs::StdRng
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng"))))]
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct ThreadRng {
// Rc is explicitly !Send and !Sync
rng: Rc<UnsafeCell<ReseedingRng<Core, OsRng>>>,
}

/// Debug implementation does not leak internal state
impl fmt::Debug for ThreadRng {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "ThreadRng {{ .. }}")
}
}

thread_local!(
// We require Rc<..> to avoid premature freeing when thread_rng is used
// within thread-local destructors. See #968.
Expand All @@ -77,13 +90,23 @@ thread_local!(
}
);

/// Retrieve the lazily-initialized thread-local random number generator,
/// seeded by the system. Intended to be used in method chaining style,
/// e.g. `thread_rng().gen::<i32>()`, or cached locally, e.g.
/// `let mut rng = thread_rng();`. Invoked by the `Default` trait, making
/// `ThreadRng::default()` equivalent.
/// Access the thread-local generator
///
/// Returns a reference to the local [`ThreadRng`], initializing the generator
/// on the first call on each thread.
///
/// For more information see [`ThreadRng`].
/// Example usage:
/// ```
/// use rand::Rng;
///
/// # fn main() {
/// // rand::random() may be used instead of rand::thread_rng().gen():
/// println!("A random boolean: {}", rand::random::<bool>());
///
/// let mut rng = rand::thread_rng();
/// println!("A simulated die roll: {}", rng.gen_range(1..=6));
/// # }
/// ```
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng"))))]
pub fn thread_rng() -> ThreadRng {
let rng = THREAD_RNG_KEY.with(|t| t.clone());
Expand All @@ -92,7 +115,7 @@ pub fn thread_rng() -> ThreadRng {

impl Default for ThreadRng {
fn default() -> ThreadRng {
crate::prelude::thread_rng()
thread_rng()
}
}

Expand Down Expand Up @@ -140,4 +163,11 @@ mod test {
r.gen::<i32>();
assert_eq!(r.gen_range(0..1), 0);
}

#[test]
fn test_debug_output() {
// We don't care about the exact output here, but it must not include
// private CSPRNG state or the cache stored by BlockRng!
assert_eq!(std::format!("{:?}", crate::thread_rng()), "ThreadRng { .. }");
}
}

0 comments on commit 766c7ec

Please sign in to comment.