API design partially based on discussions with @BartMassey.
Proposal
Problem statement
People regularly want to generate random numbers for a variety of use cases. Doing so currently requires using a crate from crates.io. rand is the 6th most downloaded crate on crates.io, and fastrand is quite popular as well. Many, many people over the years have expressed frustration that random number generation is not available in the standard library (despite the standard library using randomness internally for HashMap).
There are multiple reasons why we have not historically added this capability. Primarily, there are three capabilities people want, and those capabilities seem to present a "pick any two" constraint:
Secure random number generation
Seedable random number generation
Stability between versions of Rust when seeded
These constraints arise from the possibility of a secure random number generator potentially requiring updates for security reasons. Changing the random number generator would result in different sequences for the same seed.
In addition to that primary constraint, there have also been design difficulties: there are numerous pieces of additional functionality people may want surrounding random number generation, which makes any proposal for it subject to massive scope creep and bikeshed painting. Most notably: users of random numbers may want to represent the state of the RNG explicitly as something they can pass around, or implicitly as global state for simplicity.
This ACP proposes a solution that aims to be as simple as possible, satisfy all the stated constraints on the problem, and allow for future expansion if desired. This ACP handles the "pick any two" constraint above by explicitly marking seeding as insecure. This will allow us to keep the insecure seedable generator the same across Rust versions and targets.
Separately, ACP 393 proposes an RNG that is secure and seedable but does not guarantee identical seeding across Rust versions.
Motivating examples or use cases
Generating randomness for games (e.g. rolling dice).
Allowing "replay" of such games by providing a seed
Randomly shuffling a list
Randomly sampling elements from a set
Random test case generation / fuzz testing
Allowing reproduction of such test cases by providing a seed
Solution sketch
All of these would live in a new std::random module.
/// Trait for any type that can be generated via random number generation.
#[sealed]
trait Random { ... }
/// A seeded, insecure random number generator.
struct InsecureRng { ... }
impl InsecureRng {
/// Create a seeded random number generator.
///
/// The resulting generator is not cryptographically secure.
pub fn new_seeded_insecure(seed: u128) -> Self;
/// Generate a random value, uniformly distributed over possible values.
pub fn random<R: Random>(&mut self) -> R;
}
The trait Random (shared with ACP 393) will initially be implemented for all iN and uN integer types, isize/usize, and bool, as well as arrays and tuples of such values.
Notably, Random will not initially be implemented for floating-point values (to avoid questions of distribution), strings/paths/etc (to avoid questions of length and character selection), or char (to avoid questions of what subset of values to generate). We can consider such additions in the future; see the section on future work below.
This ACP proposes initially using the same RNG for both secure and insecure random number generation, but if we ever need to change the secure RNG, we would keep the insecure seedable RNG as a separate implementation that keeps the same behavior.
The seeded insecure random number generator, given the same seed, will provide the same sequence of random numbers, on all targets.
Alternatives
We could do nothing, and continue to refer people to external crates like rand and fastrand.
We could avoid providing seeded random number generation at all, and refer people who need seeded random number generation to external crates.
Future work
We could consider, in the future, introducing random float generation, or random character generation. We should probably leave random String generation to crates, though, since that would have many more aspects to customize.
We could consider supporting random generation in ranges (e.g. random_range(1..6)). Providing a correct implementation will steer people away from the most common open-coded implementation (using %), which introduces bias.
We could unseal the Random trait to allow implementing it. This would require defining an interface for it, and committing to the stability of that interface.
We could provide functions to fill an existing value rather than creating a new one.
We could provide a function to fill an array/slice with randomness.
We could allow #[derive(Random)] on structures or enums containing only types that implement Random.
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
We think this problem seems worth solving, and the standard library might be the right place to solve it.
We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
API design partially based on discussions with @BartMassey.
Proposal
Problem statement
People regularly want to generate random numbers for a variety of use cases. Doing so currently requires using a crate from
crates.io
.rand
is the 6th most downloaded crate oncrates.io
, andfastrand
is quite popular as well. Many, many people over the years have expressed frustration that random number generation is not available in the standard library (despite the standard library using randomness internally forHashMap
).There are multiple reasons why we have not historically added this capability. Primarily, there are three capabilities people want, and those capabilities seem to present a "pick any two" constraint:
These constraints arise from the possibility of a secure random number generator potentially requiring updates for security reasons. Changing the random number generator would result in different sequences for the same seed.
In addition to that primary constraint, there have also been design difficulties: there are numerous pieces of additional functionality people may want surrounding random number generation, which makes any proposal for it subject to massive scope creep and bikeshed painting. Most notably: users of random numbers may want to represent the state of the RNG explicitly as something they can pass around, or implicitly as global state for simplicity.
This ACP proposes a solution that aims to be as simple as possible, satisfy all the stated constraints on the problem, and allow for future expansion if desired. This ACP handles the "pick any two" constraint above by explicitly marking seeding as insecure. This will allow us to keep the insecure seedable generator the same across Rust versions and targets.
Separately, ACP 393 proposes an RNG that is secure and seedable but does not guarantee identical seeding across Rust versions.
Motivating examples or use cases
Solution sketch
All of these would live in a new
std::random
module.The trait
Random
(shared with ACP 393) will initially be implemented for all iN and uN integer types,isize
/usize
, andbool
, as well as arrays and tuples of such values.Notably,
Random
will not initially be implemented for floating-point values (to avoid questions of distribution), strings/paths/etc (to avoid questions of length and character selection), orchar
(to avoid questions of what subset of values to generate). We can consider such additions in the future; see the section on future work below.This ACP proposes initially using the same RNG for both secure and insecure random number generation, but if we ever need to change the secure RNG, we would keep the insecure seedable RNG as a separate implementation that keeps the same behavior.
The seeded insecure random number generator, given the same seed, will provide the same sequence of random numbers, on all targets.
Alternatives
We could do nothing, and continue to refer people to external crates like
rand
andfastrand
.We could avoid providing seeded random number generation at all, and refer people who need seeded random number generation to external crates.
Future work
We could consider, in the future, introducing random float generation, or random character generation. We should probably leave random String generation to crates, though, since that would have many more aspects to customize.
We could consider supporting random generation in ranges (e.g.
random_range(1..6)
). Providing a correct implementation will steer people away from the most common open-coded implementation (using%
), which introduces bias.We could unseal the
Random
trait to allow implementing it. This would require defining an interface for it, and committing to the stability of that interface.We could provide functions to fill an existing value rather than creating a new one.
We could provide a function to fill an array/slice with randomness.
We could allow
#[derive(Random)]
on structures or enums containing only types that implementRandom
.What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
Second, if there's a concrete solution: