Skip to content
CryoCryo home
StdlibNumerics

random

Two generators and a set of distributions over them. Both generators implement RandomSource, so the whole draw surface works with either — or with a source of your own.

ItemImport
RandomSourceimport std::random::source;
Rngimport std::random::rng;
SecureRngimport std::random::secure;
UniformU64, Normal, WeightedIndex, ...import std::random::distribution;
RandomErrorimport std::random::error;

Choosing a generator

Rng is xoshiro256** — excellent statistical quality, 256 bits of state, very fast. It is not suitable for cryptography.

SecureRng draws straight from the operating system's cryptographic generator. It exposes no predictable algorithm and is the right choice for keys, tokens, and nonces.

import std::random::rng;

mut r: Rng = Rng::from_seed(42);        // reproducible
const roll: u64 = r.next_range(1, 7);

Rng::from_seed(seed) gives the same stream for the same seed, which is what you want for tests and simulations. Rng::from_os() seeds unpredictably, falling back to a monotonic-clock and pid mix if the kernel source is briefly unavailable, so it never hard-fails — fine for non-cryptographic use.

Rng is POD, so copying it forks the stream rather than sharing it. SecureRng is stateless, so a copy is the same source.

RandomSource

One required primitive — next_64(mut &this) -> u64 — and a substantial set of defaults built on it.

next<T>()

A uniform draw of type T, dispatched at compile time via static match (T). Select the type with a turbofish (r.next<u32>()) or let context infer it.

TWhat you get
Any fixed-width integer, usize, isizeThe required bits, taken from the top (best-mixed) end. u128 and i128 concatenate two draws.
booleanThe top bit.
f64 / f32A uniform value in [0.0, 1.0). f64 uses the top 53 bits, so every representable value in the interval is equally likely.
charA uniform Unicode scalar; the surrogate range is skipped.

Bounded draws

  • next_below(bound: u64) -> u64 — uniform in [0, bound). Unbiased: the top partial bucket is rejected so the modulo introduces no skew. Panics when bound == 0, since an empty range has no value to return.
  • next_range<T>(lo: T, hi: T) -> T — uniform in [lo, hi), requiring lo < hi. Every integer width is covered; 64-bit-and-narrower spans go through next_below, and u128/i128 use an inline 128-bit rejection sampler.
  • next_below_wide(bound: u128) -> u128 — the same top-bucket rejection over the full 128-bit range.

Bulk and collections

  • fill(dst: Slice<u8>) — fill a byte view.
  • shuffle<T>(items: &Slice<T>) — an in-place uniform Fisher-Yates pass. Elements are swapped, never copied out, so there is no Copy bound and owning element types work.
  • choose<T>(items: &Slice<T>) -> Option<T*> — a uniformly chosen element, borrowed in place. None when empty.

SecureRng

import std::random::secure;

mut s: SecureRng = SecureRng::new();
s.fill(key.as_slice());

Because it satisfies RandomSource, the full draw surface above is available. Those defaults panic if the kernel cannot supply entropy — a broken system invariant rather than an expected failure. When you need to handle it, use try_fill(dst: Slice<u8>) -> Result<(), RandomError>, which reports it as EntropyUnavailable.

The backing source is per target: getrandom on Unix, RtlGenRandom on Windows. Rng::from_os routes through the same portable helper rather than binding the Linux-only symbol directly.

Distributions

A Distribution<T> turns a source of random bits into draws of a particular shape. sample(rng) is generic over R: RandomSource, so every distribution works with Rng, SecureRng, or a custom source.

DistributionProduces
UniformU64::new(lo, hi)u64 in [lo, hi)
UniformI64::new(lo, hi)i64 in [lo, hi)
UniformF64::new(lo, hi)f64 in [lo, hi)
Bernoulli::new(p)boolean — true with probability p, clamped to [0, 1]
Normal::new(mean, std_dev)f64
Exponential::new(lambda)f64 — waiting times at rate lambda, mean 1 / lambda
WeightedIndex::try_new(weights: Slice<f64>)u64 — an index chosen in proportion to its weight
import std::random::distribution;

const d: Normal = Normal::new(0.0, 1.0);
const z: f64 = d.sample(&r);

WeightedIndex is built once from a weight list and then sampled cheaply. It owns its cumulative table, so it carries a drop, and construction is fallible: InvalidWeights when the list is empty, contains a negative entry, or sums to zero.

RandomError

VariantMeaning
EntropyUnavailableThe kernel CSPRNG could not supply bytes. Surfaced by try_fill.
EmptyRangeA bounded draw was asked for an empty range.
InvalidWeightsWeights were empty, negative, or summed to zero.

It implements Eq and the usual describe() -> Str.