rand 0.10.3: the RngCore trait, the default feature set, and the reproducibility gap
A Rust library for random number generation.
At a glance
- What is it?
- rand is a family of Rust generators built on one trait, where rand::rng() is seeded for you through getrandom and value sampling covers uniform and non-uniform ranges. The parts worth reading first are the features you inherit by default, the nightly gate on SIMD sampling, and the fact that reproducibility covers only a subset of the crate.
- Who is it for?
- rand is the right default for Rust code that needs randomness, not a deliberate choice anybody makes, and it earns that position by refusing three goals: a small line count, a simple implementation and cryptographic status. Adopt it when you want rand::rng() seeded through getrandom and ranged sampling under one trait.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Star history
- rust-random/rand2,089 ★ · +3
| Repository | 2026-10-14 | 2026-10-23 |
|---|---|---|
| rust-random/rand | 2.1k | 2.1k |
3 Stars in the last 9 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
RngCore is the trait every generator in this family shares
The library is really a family, not one crate. Every generator implements `rand_core::RngCore`, and the higher-level `rand` crate sits on top of that trait rather than hiding it, so a generator from a sibling crate drops into code written against the main one. Alongside it sit `chacha20`, `rand_xoshiro`, `rand_pcg`, `rand_sfc` and `rand_seeder`, gathered under the rngs repository.
Three layers of use sit above the trait. Value sampling comes first, with `StandardUniform` for plain random values and `Uniform` for ranged ones. Sequence work, mostly choosing and shuffling, lives in the traits under `rand::seq`. Non-uniform distributions are split across two places, and that split is worth noticing: the project ships its own `rand_distr`, but the README also points at the third-party `statrs` for a wider catalogue.
Seeding is handled for you on the common path. `rand::rng()` is described as asymptotically fast, automatically seeded and reasonably strong, and it is available on every target with the standard library; the seed comes from the `getrandom` crate, which is also what the system generator depends on. What is left for you is a one-call default plus a trait you can implement yourself.
Default features switch on three RNG types before you ask
Add `rand` to a manifest and you get more than one algorithm. The default set is four flags, and each one pulls in a different dependency.
[features]
# Meta-features:
default = ["std", "std_rng", "sys_rng", "thread_rng"]
serde = ["dep:serde"]
# Option (enabled by default): without "std" rand uses libcore; this option
# enables functionality expected to be available on a standard platform.
std = ["alloc", "getrandom?/std"]
# Option: "alloc" enables support for Vec and Box when not using "std"
alloc = []`sys_rng` pulls in `getrandom` and turns on `rand::rngs::SysRng`. `std_rng` pulls in `chacha20` and turns on `rand::rngs::StdRng`. `thread_rng` brings the other three along with it, and it is what makes `rand::rngs::ThreadRng` and `rand::rng()` exist. Drop the std flag and the crate falls back to libcore, taking a significant portion of sequence and distribution functionality with it, because those need `alloc`, which that flag turns on in turn.
One detail catches people out. The `chacha` flag is not in the default set, so `rand::rngs::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng}` stay hidden until you ask, even though the default `std_rng` path already links that same upstream crate. You get a ChaCha-backed generator either way, just not the named types.
The published tarball carries src, not the example programs
The manifest decides what reaches a registry user, and the include list is short:
include = ["src/", "LICENSE-*", "README.md", "CHANGELOG.md", "COPYRIGHT"]Those five patterns are everything published. The example programs stay in the checkout: `examples/monte-carlo.rs`, `examples/monty-hall.rs`, `examples/print-next.rs` and `examples/rayon-monte-carlo.rs` never appear in an unpacked registry source, and neither do `benches/`, `tests/`, `utils/` or the `.github/` workflow directory. `autobenches = true` wires up the benchmark harness for a directory you will only meet in the repository.
The same manifest settles the licence, and there its own files and the hosting service disagree: the top-level entries carry LICENSE-APACHE and LICENSE-MIT with no bare LICENSE file between them, the manifest line reads `license = "MIT OR Apache-2.0"`, and the README says the project is distributed under the terms of both, so the hosting service falls back to showing the licence as Other because it cannot classify that layout.
Elsewhere the manifest is plain build metadata: `version = "0.10.3"`, `edition = "2024"`, `rust-version = "1.85"`. The repository is not archived, its last push was on 2026-09-20, the 0.10 line dates to February 2026, and the dated version record still carries 0.8.8 from 2026-08-25 alongside 0.9.5 from 2026-07-11, a detail the ascending version numbers hide on their own.
No install command, so the book and the crate page are the entry points
There is no dependency line to copy here, so the first install comes from elsewhere: the crate page on crates.io, the API reference on docs.rs, and The Rust Rand Book, which doubles as the homepage.
The first decision is which features to accept, and the second is which generator type to hold. In place of a snippet, the repository gives you four runnable examples. `examples/monte-carlo.rs` and `examples/rayon-monte-carlo.rs` show sampling at volume, the second one spreading work over the rayon thread pool. `examples/monty-hall.rs` covers selection and shuffling through `rand::seq`. `examples/print-next.rs` does the smallest useful thing. None of them ship with the crate, so run them from a repository checkout.
For documentation the manifest carries build metadata rather than a published command:
[package.metadata.docs.rs]
# To build locally:
# RUSTDOCFLAGS="--cfg docsrs -Zunstable-options --generate-link-to-definition" cargo +nightly doc --all --all-features --no-deps --open
all-features = true
rustdoc-args = ["--generate-link-to-definition"]That all-features flag has a consequence worth chasing.
simd_support needs nightly, and docs.rs turns it on anyway
Sampling SIMD integers and floats sits behind `simd_support`, which the README labels experimental. `std::simd` is not stable yet, so the feature needs nightly Rust and the README warns that it may cause build failures. On a stable toolchain that is a hard stop rather than a degraded mode.
The build metadata quoted earlier shows where this bites. `[package.metadata.docs.rs]` sets `all-features = true`, so the hosted API documentation is generated with the experimental feature switched on, while `[package.metadata.playground]` limits the playground to `serde`. Two feature sets for one crate, and neither is the default set.
The comment above that block holds the matching local command, and it passes `--all-features` too. Reproducing the hosted docs locally means using the same nightly toolchain the service uses. `serde` is a real feature defined under the meta-features heading even though the default set omits it, so a playground session will let you try serialisation that a normal build does not compile.
unbiased removes a bias of up to one in 2^48 samples
Range sampling with `Uniform` carries a documented tolerance: by default the project accepts a bias affecting no more than one in 2^48 samples. The `unbiased` feature exists for the algorithms that can do better, and the manifest repeats the same threshold in a comment on the flag.
The catch is stated right beside it. Enabling `unbiased` is expected to affect reproducibility of results. So the two options you would argue about most pull in opposite directions: the default output stays inside the portability story, and the stricter sampler gives that up. Nothing in the README quantifies how much reproducibility moves in practice, so anyone depending on byte-identical output has to measure it themselves.
The scope is narrow: one flag affecting one distribution. `StandardUniform`, the traits under `rand::seq` and the generators themselves are untouched by it, so switching it on is a reviewable change rather than a global correctness switch.
wasm32-unknown-unknown has no entropy source until you arrange one
WebAssembly is where the library stops being automatic. The WASI and Emscripten targets are directly supported, so a seeded generator works there without help. The `wasm32-unknown-unknown` target is not automatically supported, and the cause is structural rather than incidental: seeding runs through `getrandom`, and that target offers no system call the module can make on its own.
Two fixes are offered and each has a price. The first is to follow the WebAssembly section of the `getrandom` documentation and wire up an entropy source yourself. The second is to disable `sys_rng`, which drops the dependency on the operating system generator but also removes `rand::rngs::SysRng` and, because `thread_rng` turns `sys_rng` on, takes `rand::rng()` with it. Neither path documents the error text you would meet when the feature is left on unconfigured, so the failure is discovered by trying it.
For code that must load in a browser or an unbundled module, plan for that integration cost up front. The feature list does not mention it, because that list only carries the nightly requirement for SIMD.
Small, simple and cryptographic are three things rand is not
The project names its own limits, and they are the part to read before adopting it. It is not small in lines of code, because the high-level `rand` and `rand_distr` crates each carry a lot of functionality. It is not simple in implementation, with a focus on correctness, speed and flexibility but not simplicity. And it is not primarily a cryptographic library: some generators aim at unpredictable output under certain constraints, SECURITY.md holds the detail, and users are expected to judge their own security requirements against it. That last point is the one to internalise, because a review that counts a rand call as a CSPRNG call reaches the wrong conclusion.
For a small-and-simple alternative, the README names fastrand and oorandom directly. The difference is not tuning, it is scope, since those exist for the case where you want randomness without this feature set, and rand drops the small-and-simple goal on purpose.
Distributions split along the same line. `rand_distr` is the project's own catalogue and `statrs` is the external one, so a codebase can carry either or both, and the README treats them as interchangeable sources of non-uniform samplers rather than pushing one of them.
Editorial conclusion
rand is the right default for Rust code that needs randomness, not a deliberate choice anybody makes, and it earns that position by refusing three goals: a small line count, a simple implementation and cryptographic status. Adopt it when you want rand::rng() seeded through getrandom and ranged sampling under one trait. Verify three things before the first merge: whether your build target is wasm32-unknown-unknown, whether simd_support stays off on a stable toolchain, and whether pinned output survives a version bump.
Frequently asked questions
What is the difference between rand::rng() and StdRng?
rand::rng() hands back a ThreadRng that is automatically seeded on every target with standard library support, and it comes from the thread_rng feature. StdRng is the chacha20-backed type you construct and seed yourself, enabled by std_rng.
Is rand safe for generating cryptographic keys?
The README says rand is not primarily a cryptographic library. Some generators aim at unpredictable value generation under certain constraints and SECURITY.md holds the detail, but users are expected to determine for themselves whether the functionality meets their own security requirements.
Which feature flags does rand enable by default?
Four: std, std_rng, sys_rng and thread_rng. The thread_rng flag brings the other three along, alloc is turned on by std, and the optional chacha flag that exposes the named ChaCha8, ChaCha12 and ChaCha20 types is not part of the default set.
Why does rand need extra setup on wasm32-unknown-unknown?
Seeding goes through the getrandom crate, and that target has no entropy source the module can reach by itself, so it is not automatically supported. Either follow the WebAssembly section of the getrandom documentation or disable the sys_rng feature, which also removes rand::rng().
Does rand produce identical output across platforms and versions?
Only a subset of the crate aims at reproducibility, and the book has a Portability chapter on it. Enabling the unbiased feature is expected to affect reproducibility of results, and reproducible runs need fixed inputs plus a commitment to algorithm stability rather than a blanket promise of identical output.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/rust-random-rand)Star history chart for your README
[](https://hysenlabs.com/projects/rust-random-rand)Was this useful?