ScreenshotNeo

BlogHow-to

How to Use Thread_rng in Rust

Use rand 0.10.3's thread-local RNG correctly, avoid version traps, and handle ranges, threads, seeding, security, and errors.

By the ScreenshotNeo team1 October 20265 min read

How to Use Thread_rng in Rust

In current rand 0.10.3, use rand::rng() to obtain the current thread’s ThreadRng. Bind the handle as mutable, then call random() or random_range(...). The spelling thread_rng() belongs to older rand APIs, so check the version in Cargo.toml before copying code.

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={coin_flip}, die_roll={die_roll}");
}

The current accessor and method names are documented in the rand::rng() documentation. The ThreadRng type and its threading and reseeding behavior are described in the ThreadRng documentation.

1. Add rand and verify your version

In a new project, add rand with Cargo:

cargo add rand

Then inspect the resolved version:

cargo tree -i rand
# or inspect Cargo.lock and Cargo.toml

This guide follows the current 0.10.x API shown by the official docs. Historical releases may require rand::thread_rng() and a different trait import. Use the documentation matching your locked dependency version.

2. Generate random values with a local ThreadRng handle

Any supported type

use rand::RngExt;

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

    let byte: u8 = rng.random();
    let probability: f64 = rng.random();
    let enabled: bool = rng.random();

    println!("byte={byte}, probability={probability}, enabled={enabled}");
}

random() uses the type annotation to determine the output type. If Rust cannot infer the type, add an explicit annotation such as let value: u32 = rng.random();.

Generate an integer in a range

use rand::RngExt;

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

    let inclusive_die = rng.random_range(1..=6); // 1 through 6
    let exclusive_index = rng.random_range(0..10); // 0 through 9

    println!("{inclusive_die} {exclusive_index}");
}

A range with ..= includes its upper bound. A range with .. excludes it. The range must be valid for the chosen type; an empty or reversed range can panic.

Choose a random item

use rand::RngExt;

fn main() {
    let mut rng = rand::rng();
    let colors = ["red", "green", "blue"];
    let index = rng.random_range(0..colors.len());
    println!("{}", colors[index]);
}

Shuffle a collection

use rand::seq::SliceRandom;

fn main() {
    let mut rng = rand::rng();
    let mut values = vec![1, 2, 3, 4, 5];
    values.shuffle(&mut rng);
    println!("{values:?}");
}

Keep the handle mutable because random generation advances the generator state.

3. Convenience functions for one-off calls

For occasional values, rand also exposes convenience functions:

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

These are shorthand over the thread-local generator. For repeated calls, create one local handle instead. The rand documentation recommends this because it avoids an initialization check on every use. See the random() and random_range() pages.

4. What ThreadRng means for threads

ThreadRng is a handle to a lazily initialized generator local to the current thread. Its handle is not Send or Sync, so do not store it in a structure that crosses thread boundaries or move it into another thread.

Each thread creates its own ThreadRng handle instead of sharing one across workers.
Each thread creates its own ThreadRng handle instead of sharing one across workers.

Create a handle inside each worker instead:

use rand::RngExt;
use std::thread;

fn main() {
    let handles: Vec<_> = (0..4)
        .map(|worker| {
            thread::spawn(move || {
                let mut rng = rand::rng();
                let value: u32 = rng.random_range(0..100);
                println!("worker {worker}: {value}");
            })
        })
        .collect();

    for handle in handles {
        handle.join().unwrap();
    }
}

This avoids sharing a non-thread-safe handle while retaining one generator per thread.

5. Seeding, reseeding, and process forks

The current documentation says that ThreadRng is automatically seeded from SysRng and reseeded after every 64 kB of output. It does not automatically reseed after a process fork. If your program forks, explicitly reseed in the child process according to your process model.

Initial seeding can panic if the operating system random source (SysRng) fails. Treat that as startup failure rather than assuming a fallback sequence.

6. Security and reproducibility decisions

  • General-purpose randomness: ThreadRng is convenient and fast, but evaluate it against your threat model.
  • Operating-system randomness: use SysRng when you need a stateless interface over the OS random source.
  • Reproducible tests or simulations: use an explicitly seeded generator. Standard generators are deterministic, but sequences are not promised to remain portable across rand releases or platforms.

Rand’s documentation says it leaves the security decision to the user. It also notes that in-memory state is not otherwise protected or required to be zeroed at process or thread exit. Do not claim that ThreadRng satisfies every cryptographic use case. Avoid calling it from an interrupt, and do not rely on reentrant use of the same generator.

7. Common errors and fixes

Symptom Cause Fix
cannot find function rng Your project uses an older rand release. Check Cargo.lock; use the version-matched docs. Older code may use thread_rng().
no method named random The extension trait is not in scope. Add use rand::RngExt; for the current API.
Borrowing or mutability error The RNG binding was not declared mutable. Declare let mut rng = rand::rng();.
Range panic The range is empty, reversed, or invalid for the target type. Validate bounds and choose .. versus ..= deliberately.
Thread transfer compile error ThreadRng is not Send or Sync. Create a separate handle inside each thread.
Startup panic while creating RNG SysRng failed during automatic seeding. Surface the startup error and investigate OS entropy availability.

8. Performance and reliability notes

For a loop, initialize one local handle outside the loop:

ScreenshotNeo removes common overlays before returning the captured page.
ScreenshotNeo removes common overlays before returning the captured page.
use rand::RngExt;

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

    for _ in 0..1_000_000 {
        total += rng.random_range(0..10) as u64;
    }

    println!("{total}");
}

This follows the documented recommendation to avoid repeated initialization checks. Keep the handle scoped to the thread doing the work. The 64 kB reseeding interval is an operational behavior documented by rand, not a throughput benchmark.

Or skip the browser setup

If your Rust service needs screenshots of generated results, documentation pages, or test output, ScreenshotNeo provides a website screenshot API and MCP server. You can call it directly without managing a browser:

cURL

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

Python

import requests

r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. Cookie banners, newsletter popups and chat widgets are removed before the shot. Bot checks, blank pages and failed loads are never billed. Its MCP server lets AI agents take screenshots, and 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account.

FAQ

Is ThreadRng cryptographically secure?

Suitability depends on your threat model. The rand documentation recommends making that determination yourself and points to SysRng as an alternative.

Can I use one ThreadRng from multiple threads?

No. Its handle is not Send or Sync. Create one handle inside each thread.

Should I call rand::random() in a hot loop?

Use a mutable local handle for repeated generation; the official docs recommend it to avoid an initialization check on every call.

Why does code using thread_rng fail on rand 0.10?

The current API documents rand::rng(). Check your dependency version and follow the matching documentation rather than mixing examples from different releases.

Does ThreadRng reseed after fork?

No automatic fork reseeding is documented. Explicitly reseed in a child process when your application forks.