Skip to content

std: simplify the random feature#157573

Open
joboet wants to merge 1 commit into
rust-lang:mainfrom
joboet:min_random
Open

std: simplify the random feature#157573
joboet wants to merge 1 commit into
rust-lang:mainfrom
joboet:min_random

Conversation

@joboet
Copy link
Copy Markdown
Member

@joboet joboet commented Jun 7, 2026

Tracking issue: #130703

This PR is a proposal to resolve all of the concerns around the random feature by removing the most controversial parts in a future-compatible fashion.

RandomSource

The main part of this proposal concerns RandomSource/Rng: I think it should be removed entirely, at least for the time being.

Random number generators are used in highly different scenarios with different requirements:

  • Security-related code like e.g. encryption libraries need nonces and keys of high cryptographic strength without a need for reproducibility.
  • Things like e.g. map generators in games need high reproducibility across different architectures and versions, as well as good performance, but do not need any security guarantees.

Trying to fulfil both needs with a single API leads to problems like whether to provide generator methods for common numeric types (#157193), which endianness to choose when constructing those types from a byte buffer (#157430). I do not know whether a one-size-fits-all-solution to these kind of problems exists, but past evidence from the rand crate indicates that this requires loads of experimentation. In my opinion, std isn't a good place for that.

Thus this PR removes RandomSource and DefaultRandomSource in favour of a single function:

// in std::random

fn fill(bytes: &mut [u8]);

This is equivalent to the previous DefaultRandomSource::fill_bytes method (I've left the choice of entropy sources unchanged). Should we, at some point, reintroduce RandomSource, this method is still very useful as a shorthand, so this is entirely future-compatible.

Having such an API in std in general is highly desired and generally uncontroversial, with the exception of three issues:

  • fallibility
  • the ability to plug in a different source on niche platforms like JS/WASM
  • dealing with uninitialised buffers

Pluggability can be left for future consideration: once EIIs are a thing this will become much easier. The same applies to uninitialised buffers: even if we add a fill_buf function taking BorrowedCursor later, the standard fill function will remain useful. Fallability is a more difficult question though:

Fallibility

Contrary to the getrandom crate, std::random::fill does not return an error. Random number generation is infallible on nearly all targets supported by std:

  • Linux's getrandom might block for a long time, but does not fail for reasons that cannot be handled by std.
  • Windows's ProcessRng always succeeds.
  • macOS's CCRandomGenerateBytes can theoretically fail, but does not in practise.
  • The arc4random_buf API used on most UNIXes cannot return an error.
  • Neither can Fuchsia's cprng_draw

And in general, if there is at least some source of entropy on the platform, waiting until enough entropy has been acquired to seed a CSPRNG is always an option. The only problematic platform I've encountered so far is ESP-IDF which does have an RNG but does not have a way of confirming the quality of it (see #157566). I don't think we should adjust the API to work around these weird platforms if nearly every other one does not have issues.

Thus std::random::fill is kept infallible.

Distribution

Providing std::random::fill alone provides the risk that users will attempt to use hacky, incorrect ways of generating numbers in a specific range. Thus, this PR preserves the Distribution trait and the random function to provide an easy, correct way of sampling in a restricted range. While this PR does not implement Distribution for types other than RangeFull, I think an implementation for other ranges should be merged before stabilisation.

std would be in good company with this API: e.g. most UNIXes have arc4random and arc4random_uniform for this kind of sampling alongside arc4random_buf.

In order to leave room for readding a RandomSource/Rng in the future, Distribution is marked as sealed for the time being. This still permits all the simple uses for numeric types.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 7, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Jun 7, 2026

r? @clarfonthey

rustbot has assigned @clarfonthey.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: @ChrisDenton, libs
  • @ChrisDenton, libs expanded to 11 candidates
  • Random selection from Mark-Simulacrum, aapoalas, clarfonthey, jhpratt

@joboet joboet added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. I-libs-api-nominated Nominated for discussion during a libs-api team meeting. labels Jun 7, 2026
@hanna-kruppe
Copy link
Copy Markdown
Contributor

I would also prefer something like this over trying to stabilize a trait for random sources as well. Even if we take as given that std will grow such a trait eventually, I think std::random::fill_bytes existing alongside it not just fine but useful, for reasons I described in rust-lang/libs-team#620 (comment) (better for no_std ecosystem, progressive disclosure, encouraging trait-based dependency injection has downsides).

I have not previously advocated for including fn random<T> but it's a good point that we can support random(1..10) and such without committing to any traits if we keep Distribution sealed (and its method unstable). I'm not super worried about people abusing fill_bytes to build the equivalent of random(..) % 10, because that involves so much boilerplate that it gently encourages using rand anyway. But if we stabilize fn random<T> at all, then we should also add the other ranges first to avoid making random(..) % n look appealing.

Comment thread library/std/src/random.rs
#[unstable(feature = "random", issue = "130703", reason = "outstanding API concerns")]
pub trait Distribution<T>: private::Sealed<T> {
/// Samples a random value from the distribution.
fn sample(&self) -> T;
Copy link
Copy Markdown
Contributor

@hanna-kruppe hanna-kruppe Jun 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: if we want to stabilize fn random without committing to this method signature, it's not enough to keep Distribution sealed, the method also has to be unstable. Or we could keep Distribution unstable entirely, but in that case we don't strictly need to seal it.

View changes since the review

@joboet
Copy link
Copy Markdown
Member Author

joboet commented Jun 7, 2026

I should say that this PR implements the most drastic version of this entire thing. It's totally reasonable to just add std::random::fill and stabilise that while still keeping RandomSource/Rng around for experimentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

I-libs-api-nominated Nominated for discussion during a libs-api team meeting. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants