Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

With current rand 0.10.3, obtain the thread-local generator with rand::rng(), bind it as mutable, and call random() or random_range(). The older-looking thread_rng() name belongs to earlier API versions, so check the rand version in your Cargo.toml before copying examples.

use rand::RngExt;

fn main() {
    let mut rng = rand::rng();
    let coin_flip: bool = rng.random();
    let die_roll: i32 = rng.random_range(1..=6);
    println!("{coin_flip}, {die_roll}");
}

This article explains setup, ranges, repeated generation, thread boundaries, seeding, security limits, errors, and the older thread_rng spelling.

Check which rand API your project uses

Rust code is version-sensitive because the accessor changed. The current rand::rng() documentation and ThreadRng documentation are for rand 0.10.3. The old spelling thread_rng() may still appear in examples written for earlier releases.

Inspect Cargo.toml and the lockfile

[dependencies]
rand = { version = "0.10.3", features = ["thread_rng"] }

Use the version selected by your project rather than blindly replacing it. The current thread-local accessor and convenience functions are exposed through the thread_rng crate feature. If your dependency is pinned to an older major or minor release, open that release’s documentation and use its API.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why the name matters

In rand 0.10.3, rand::rng() returns a handle to the current thread’s ThreadRng. Code that calls rand::thread_rng() can fail to compile when it is moved to a release whose public API uses rng().

Generate random values with a local ThreadRng

Complete example

use rand::RngExt;

fn main() {
    let mut rng = rand::rng();

    let byte: u8 = rng.random();
    let fraction: f64 = rng.random();
    let die: i32 = rng.random_range(1..=6);

    println!("byte: {byte}");
    println!("fraction: {fraction}");
    println!("die: {die}");
}

The type annotation tells random() which supported type to produce. In this example it generates an unsigned byte, an f64, and an integer die result.

Why the binding must be mutable

Random generation advances the generator state, so methods are called through a mutable handle. Declare it with let mut rng; otherwise Rust reports that a mutable borrow is required.

Generate booleans and other values

use rand::RngExt;

fn main() {
    let mut rng = rand::rng();
    let enabled: bool = rng.random();
    let sample: u32 = rng.random();
    println!("enabled={enabled}, sample={sample}");
}

The random() method uses the destination type to select the distribution supported for that type. Keep the annotation when inference cannot determine the type.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Generate a number in a range

Inclusive and exclusive bounds

use rand::RngExt;

fn main() {
    let mut rng = rand::rng();
    let inclusive = rng.random_range(1..=6); // 1 through 6
    let exclusive = rng.random_range(0..10); // 0 through 9
    println!("{inclusive} {exclusive}");
}

1..=6 includes both endpoints, which is useful for a six-sided die. 0..10 excludes 10, producing values from 0 through 9. The range’s element type is inferred from its bounds and the receiving variable.

Ranges for other numeric types

use rand::RngExt;

fn main() {
    let mut rng = rand::rng();
    let percentage: f64 = rng.random_range(0.0..=100.0);
    let offset: i16 = rng.random_range(-20..=20);
    println!("{percentage:.2}% ({offset})");
}

Choose bounds that are valid for the target type. An empty or invalid range is a programming error; validate values before constructing a range supplied by user input.

Reuse one handle for repeated calls

For a loop or a function that generates many values, create one local handle and reuse it:

use rand::RngExt;

fn main() {
    let mut rng = rand::rng();
    let mut rolls = [0_i32; 10];

    for roll in &mut rolls {
        *roll = rng.random_range(1..=6);
    }

    println!("{rolls:?}");
}

The rand documentation recommends a local binding for repeated calls because it avoids an initialization check on every use. Keep the handle in the scope that performs the work instead of repeatedly calling the accessor.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Shuffle a collection

use rand::seq::SliceRandom;

fn main() {
    let mut rng = rand::rng();
    let mut cards = ["A", "K", "Q", "J"];
    cards.shuffle(&mut rng);
    println!("{cards:?}");
}

Pass the mutable handle to APIs such as shuffle. The exact trait import can vary with the rand release, so follow the versioned sequence documentation if your compiler reports an unresolved import.

Use convenience functions for one-off values

The rand::random() documentation provides a shorthand for common types, and rand::random_range() is shorthand for calling rng().random_range(range).

fn main() {
    let coin: bool = rand::random();
    let roll: i32 = rand::random_range(1..=6);
    println!("{coin}, {roll}");
}

These functions are convenient for isolated calls. For a loop, prefer one mutable local handle so the thread-local lookup and initialization check are not repeated.

Understand ThreadRng’s thread and lifecycle rules

It belongs to the current thread

ThreadRng is a reference to a lazily initialized thread-local generator. Its handle is neither Send nor Sync, so do not move it into another thread or store it in a structure that must cross thread boundaries. Create a separate handle inside each worker:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
use std::thread;
use rand::RngExt;

fn main() {
    let worker = thread::spawn(|| {
        let mut rng = rand::rng();
        let value: u32 = rng.random();
        value
    });

    println!("{}", worker.join().expect("worker panicked"));
}

Move ordinary generated values between threads after generation, not the ThreadRng handle itself.

Initialization, reseeding, and fork

The generator is initialized lazily and seeded from SysRng. The current documentation describes reseeding after every 64 kB of output, but it does not automatically reseed after a process fork. If your program forks, explicitly reseed in the child according to the versioned rand API.

Initial seeding can panic if the operating-system random source (SysRng) fails. Treat that as a startup failure and investigate the host’s entropy source rather than assuming the generator returned a predictable fallback.

Reentrancy and interrupts

Calls are not reentrant-safe. Do not call the same generator from an interrupt or signal context unless you can rule out concurrent use of that generator.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the right randomness source

ThreadRng for general application randomness

ThreadRng is convenient, fast for general-purpose use, thread-local, automatically seeded, and periodically reseeded. Those properties make it a practical default for simulations, randomized UI behavior, sampling, and games.

SysRng when you need the operating-system source directly

The Rand RNG module overview describes SysRng as a stateless interface over the operating system’s random source and identifies it as the source used to seed ThreadRng. Use it when your design calls for direct operating-system randomness rather than a cached thread-local generator. See the Rand RNG module documentation.

Seeded generators for reproducible sequences

An explicitly seeded generator is appropriate for repeatable tests or simulations. Standard generators are deterministic, but the RNG module documentation warns that sequences are not portable across all releases and platforms. Record the rand version, algorithm, and seed when reproducibility matters.

Security qualifications you should not skip

Rand describes the current thread generator as ChaCha-based, fast, and reasonably secure, but leaves it to the user to decide whether it meets a particular threat model. It does not provide additional protection for in-memory state and is not required to zero that memory at process or thread exit.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Therefore, do not make blanket claims that ThreadRng is suitable for every cryptographic or secret-handling task. Define the attacker, lifetime, process model, and state-protection requirements first; use SysRng or a specialized design when those requirements call for it.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot common compile and runtime problems

cannot find function thread_rng

Cause: The example targets an older API or the needed feature is disabled.

Fix: Check the selected rand version, enable the thread_rng feature where appropriate, and use let mut rng = rand::rng(); for rand 0.10.3.

no method named random or random_range

Cause: The extension trait is not in scope.

Fix: Add use rand::RngExt; for the current documented API. If your project uses an older rand release, consult that release’s trait name and method documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Borrowing or mutability errors

Cause: The handle was declared with let rng or borrowed immutably.

Fix: Declare it as let mut rng and pass &mut rng to APIs such as shuffle.

Thread-safety trait errors

Cause: A ThreadRng handle is being moved into another thread or shared across threads.

Fix: Construct rand::rng() inside each thread and send only the resulting data.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Unexpected panic during startup

Cause: Initial seeding through SysRng failed.

Fix: Check operating-system entropy availability, container or sandbox restrictions, and host errors. Do not silently substitute a predictable source for security-sensitive work.

Or skip the browser setup

If your Rust project also needs website screenshots for documentation, testing, or generated reports, ScreenshotNeo provides a one-request API instead of maintaining browser automation:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for parameters and response details. Before capture, it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. It also offers an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

The Bottom Line

For rand 0.10.3, use a mutable rand::rng() handle with RngExt::random and random_range. Keep the handle within its thread, verify the crate version before using older thread_rng() examples, and choose SysRng or a seeded generator when your threat model or reproducibility requirements demand it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.