🎓 A1Learner
🔍

Rust Choosing a Generator

A generator kind is one member of the generator family in rand. Each kind trades speed, size and safety in a different way.

The per-thread one

rand::rng() is a per-thread generator. The system seeds it for you. No two runs match.

let mut rng = rand::rng();
let roll = rng.random_range(1..=6);

The standard generator, seeded

StdRng is the strong all-purpose generator. A seed makes it replay one fixed stream.

let mut rng = StdRng::seed_from_u64(42);
println!("roll = {}", rng.random_range(1..=6));

The small generator, seeded

SmallRng keeps far less state. It draws fast and suits games and simulations.

let mut rng = SmallRng::seed_from_u64(42);
println!("roll = {}", rng.random_range(1..=6));

Seed any generator from the system

from_rng fills a seed from another generator. Feed it the thread generator and the new one starts from system randomness.

let mut mine = StdRng::from_rng(&mut rand::rng());

Same seed, same stream

Two generators with one seed walk in lockstep. Tests lean on this fact hard.

let mut a = StdRng::seed_from_u64(7);
let mut b = StdRng::seed_from_u64(7);
let first: u32 = a.random();
let second: u32 = b.random();
println!("same stream = {}", first == second);

Example

// Cargo.toml: rand = "0.10"
use rand::prelude::*;
use rand::rngs::{SmallRng, StdRng};
use rand::SeedableRng;

fn main() {
    let mut standard = StdRng::seed_from_u64(42);
    let mut small = SmallRng::seed_from_u64(42);
    for n in 1..=3 {
        println!(
            "round {n}: std = {} small = {}",
            standard.random_range(1..=6),
            small.random_range(1..=6)
        );
    }
    let mut replay = StdRng::seed_from_u64(42);
    println!("std replay matches = {}", replay.random_range(1..=6) == 1);
}

Key points

  • Thread generator: rand::rng(), seeded by the system and reseeded as it runs
  • StdRng: the standard generator.
  • It runs the ChaCha12 algorithm and holds the CryptoRng mark, so it may guard secrets
  • SmallRng: a tiny, fast generator built on xoshiro.
  • It holds no CryptoRng mark.
  • Keep it away from tokens and keys
  • Seed: the starting number of a stream.
  • Same seed, same stream, on every machine
  • Use seed_from_u64 to seed from one number.
  • Use from_seed when you hold the full seed bytes

In this example, both generators start from seed 42. They still disagree on every roll. They run different algorithms, so one seed feeds two different streams. The standard generator rolls 1, 4, 2. The small one rolls 5, 2, 6. A third standard generator then starts from seed 42 again. Its first roll is 1, just like the first one. The replay matches, and that is the whole point of a seed.

Note

A seed is not a password.

Anyone who knows the seed can replay the whole stream.

Real secrets need fresh system randomness, never a seed you picked

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

A test must roll the same dice on every run. What do you reach for?