Randomness is an invaluable part of both web development and our lives today. Without randomness, Bitcoin and cryptocurrencies, the banking system, a game of chance like dice or roulette wheel, and, of course, all cryptographic operations, would be predictable and insecure.
In Rust, there are several random number generator implementation crates. Below, we’ll look at the popular random number generators, compare and contrast them, and explore their advantages and disadvantages and their use cases.
Note: Unlike JavaScript, there is no Math.random
method equivalent currently available in Rust.
Rand is the most popular random number generator crate in the Rust ecosystem and is maintained by rust-random and 229 contributors. It has a GitHub rating of over 1,090 stars. Rand offers many features that you can use for different cases of randomness within Rust, and it currently uses the ChaCha block cipher algorithm with 12 rounds, which is fast, statistically strong, and unpredictable so that it can adequately generate cryptographically secure pseudorandom numbers (CSPRNG).
Rand has a small file size of 87.1KB and a significant amount of downloads. As of the time of this writing, it has been downloaded over 116,114,195 times.
Deterministic random numbers are generated from a seed — a set of defined numbers. Anyone with that seed can re-generate the same number. Take the piece of code below, for example:
use rand_chacha; fn generate_random_numbers_with_a_seed(seed : u64) { let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed); println!("Deterministic Random numbers with seed:{}", rng.gen::<i32>()); } fn main(){ generate_random_numbers_with_a_seed(200); //Result : -416273517 }
The result will always be -416273517
as long as you are using the seed 200
. To make this example work, you’ll need to add rand_chacha
as a dependency to your Cargo.toml
file, like so:
[dependencies] rand_chacha = "0.3.1"
In previous Rand versions, you only needed to install Rand to use rand_chacha
because it was a dependency. But, as of August 2019, rand_chacha
is separate because the team was looking to reduce dependencies.
Rand also allows you to create random numbers without a seed because it’s seeded by the system and re-seeded only when necessary. You can trigger the re-seeding process manually, like this:
use rand::rngs::OsRng; use rand::rngs::adapter::ReseedingRng; use rand::prelude::*; use rand_chacha::ChaCha20Core; fn main(){ let prng = ChaCha20Core::from_entropy(); let mut reseeding_rng = ReseedingRng::new(prng, 0, OsRng); //Reseeding println!("Random number: {}", reseeding_rng.gen::<u64>()); }
Re-seeding improves your security in case of a side-channel attack and/or misuse. Essentially, re-seeding means you generate a new seed from an entropy generated by your system.
However, in most cases, you might use the thread-local generator thread_rng()
more often to generate random numbers with Rand. This is because it’s usually sufficient for generating the randomness you need (and is seeded by your system). The key is that you must use a new seed for every random number. Here’s an example using the thread-local generator in Rand.
let mut rng = rand::thread_rng(); println!("{}", rng.gen::<i32>()); //Result is an unknown value println!("{}", rng.gen::<i64>()); //Result is an unknown value println!("{}", rng.gen::<u32>()); //Result is an unknown value println!("{}", rng.gen::<u64>()); //Result is an unknown value
There are several functions within the Rand crate — check Rand’s documentation to learn more.
So, what are the ups and downsides to using Rand within your Rust application? Mostly, Rand has almost everything you need to work with secured randomness, which is great. If, however, you are looking to implement basic randomness without worrying too much about complexity or security, Rand might be overkill for your project.
Let’s cover some use cases for Rand below. With Rand, you can:
.gen_range(1.0..100.0)
:
let mut rng = thread_rng(); let random_number = rng.gen_range(1.0..100.0); println!("Random from range {}", random_number);
let mut rng = rand::thread_rng(); let mut arr = (1..100).collect::<Vec<i32>>(); arr.shuffle(&mut rng); println!("Shuffle array: {:?}", arr);
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1000); println!("Deterministic Random numbers with seed:{}", rng.gen::<i32>()); // Ghan
fn flip_coin() { let mut rng = rand::thread_rng(); if rng.gen::<bool>() { println!("heads"); } else { println!("tails"); } }
There is so much more you can do with Rand, but you should check the docs to learn more.
Fastrand is a small and fast random number generator based on WyRand, which is a modern non-cryptographic hash function and pseudo-random number generator. It was released two years ago, and, unlike Rand, Fastrand is not cryptographically secure, so it shouldn’t be used for that purpose. It also only has one dependency, Instant
, whereas Rand has six.
Fastrand’s random numbers are only generated based on a predetermined seed so that anyone with that seed can generate the same number.
Fastrand is simple and compact. Most importantly, it’s fast. However, Fastrand is not cryptographically secure. so if you are looking for something more secure to use for cryptography, use Rand instead.
At the time of this writing, there are no Stack Overflow questions on using Fastrand, and there are only a few issues on GitHub. The project has over eight contributors and 10,976,949 downloads at the moment. When compared to other random number generators, the community around Fastrand is growing fast, but not as fast as Rand.
Let’s see some examples of what you can do with Fastrand.
true
or false
value, which will be good for a coin-flipping scenario.
// Flip a coin: if fastrand::bool() { println!("Fast Rand: heads"); } else { println!("Fast Rand: tails"); }
println!("Fast Rand: Unsigned Random number 32bit: {}", fastrand::u32(..)); println!("Fast Rand: Unsigned Random unsigned number 64bit: {}", fastrand::u64(..)); println!("Fast Rand: Signed Random number 64bit: {}", fastrand::i64(..)); }
let i = fastrand::usize(vec![1,3,5,6,7,8]); let element = arr[i]; println!("Fast Rand: Random number from array {:?}: is {}", arr, elem);
fn shuffle(mut arr: Vec<u32>) { let shuffled = fastrand::shuffle(&mut arr); println!("Random numbers from Shuffled array {:?}", shuffled); }
You should check the documentation for more use cases.
Nanorand is an interesting random number generator that is simple, even though it allows you to use over one algorithm. You can choose which algorithm you want to use depending on your needs.
First and foremost, Nanorand allows you to use multiple algorithms, such as WyRand
, ChaCha
, and Pcg64
.
So, whether you are looking to generate a cryptographically secure PRNG or a non-secure random number with a quick compile time, you can get it done using Nanorand.
Nanorand is lightweight, about 18.4KB, and fast, depending on the type of random numbers you are trying to generate. Remember: cryptographically secure random numbers will be much slower to generate than non-cryptographically secure random numbers.
Currently, Nanorand does not have much community support, and the documentation does not cover other algorithms it implements apart from WyRand
. However, Nanorand has been installed over 834,185 times, has 11 contributors, and has few issues on GitHub.
Nanorand’s use cases are like other random numbers generators we’ve discussed already.
u64
, u32
, i32
, etc.
let mut rng = WyRand::new(); println!("Nano Rand: random number 64bit: {}", rng.generate::<u64>()); println!("Nano Rand: random number 32bit: {}", rng.generate::<u32>());
let mut rng = WyRand::new(); let mut items = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; rng.shuffle(&mut items); println!("Nano: Shuffled array vec {:?}", items);
let mut rng = WyRand::new(); println!( "Random number between 1 and 100: {}", rng.generate_range(1_u64..=100) );
Oorandom is a deterministic random number generator that implements permuted congruential generator (PCG). Use oorandom if you find the rand crate to be too large for your project and just want something more compact and specific. More so, it’s #[no_std]
, which is great if you are building a project for bare-metal devices.
To generate a random number, you will need to generate it from a seed. If you want to generate a non-deterministic random number, use the getrandom
crate to generate random seeds from your system.
Oorandom is, unfortunately, not cryptographically secure.
Oorandom is quite popular, as it’s been installed over 8,107,644 times. For most people who need a simple random number generator, oorandom is a great option.
Oorandom implements just a handful of functions: rand_float
, rand_i32
, rand_range
, and rand_u32
.
Here is an example of how you can use it:
use oorandom; fn main(){ let seed = 4; let mut rng = oorandom::Rand32::new(seed); println!("OORandom: Random number 32bit: {}", rng.rand_i32()); println!("OORandom: Random number range: {}", rng.rand_range(1..100)); println!("OORandom: Random number with float: {}", rng.rand_float()); }
With Rust, there is so much we can do by using random number generators. While the generators on this list may change over time as stronger algorithms are created, we discussed some of the go-to options for devs working with Rust, security, and/or cryptography in this article. Happy hacking!
Debugging Rust applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking the performance of your Rust apps, automatically surfacing errors, and tracking slow network requests and load time, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your Rust application. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Modernize how you debug your Rust apps — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.