From 1f4b26c603861b8844c5ee8f6b125f08c00c5075 Mon Sep 17 00:00:00 2001 From: Hanna Kruppe Date: Sun, 31 May 2026 14:03:13 +0200 Subject: [PATCH 1/2] add RandomSource::next_u{32,64} methods If we're going to add these methods at any point, it's better to add them before stabilization. Since they should be allowed to behave differently from `fill_bytes` calls, existing code that calls `fill_bytes` (including the standard library's `Distribution` impls) would break reproducibility by switching to `next_uN` later. Similarly, `RandomSource` implementations that want to guarantee reproducibility either have to override these methods from the start or never override them. The main reason why we should add these methods is, of course, performance. The existing contract of `fill_bytes` helps when an `impl RandomSource` generates one word at a time since `fill_bytes` calls that generate exactly one word's worth of data can be inlined and simplified to avoid the general loop. However, for `dyn RandomSource`, the `fill_bytes` call can't be inlined, so the desired optimization doesn't kick in. In contrast, `next_uN` methods in the vtable make direct, inlinable calls to `fill_bytes`. --- library/core/src/random.rs | 39 ++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/library/core/src/random.rs b/library/core/src/random.rs index 06f4f30efe2b5..99517a3da6415 100644 --- a/library/core/src/random.rs +++ b/library/core/src/random.rs @@ -12,6 +12,30 @@ pub trait RandomSource { /// cases. For instance, this allows a `RandomSource` to generate a word at a time and throw /// part of it away if not needed. fn fill_bytes(&mut self, bytes: &mut [u8]); + + /// Returns a random 32-bit integer. + /// + /// The default implementation uses `fill_bytes` and interprets those bytes as integer, but this + /// is not guaranteed. A `RandomSource` can override this method to behave differently, e.g., it + /// may skip parts of its internal buffer to avoid the need for an unaligned load. + #[inline] + fn next_u32(&mut self) -> u32 { + let mut buf = [0; size_of::()]; + self.fill_bytes(&mut buf); + u32::from_ne_bytes(buf) + } + + /// Returns a random 64-bit integer. + /// + /// The default implementation uses `fill_bytes` and interprets those bytes as integer, but this + /// is not guaranteed. A `RandomSource` can override this method to behave differently, e.g., it + /// may skip parts of its internal buffer to avoid the need for an unaligned load. + #[inline] + fn next_u64(&mut self) -> u64 { + let mut buf = [0; size_of::()]; + self.fill_bytes(&mut buf); + u64::from_ne_bytes(buf) + } } /// A trait representing a distribution of random values for a type. @@ -35,6 +59,13 @@ impl Distribution for RangeFull { } macro_rules! impl_primitive { + ($t:ty, $method:ident) => { + impl Distribution<$t> for RangeFull { + fn sample(&self, source: &mut (impl RandomSource + ?Sized)) -> $t { + source.$method() as $t + } + } + }; ($t:ty) => { impl Distribution<$t> for RangeFull { fn sample(&self, source: &mut (impl RandomSource + ?Sized)) -> $t { @@ -50,10 +81,10 @@ impl_primitive!(u8); impl_primitive!(i8); impl_primitive!(u16); impl_primitive!(i16); -impl_primitive!(u32); -impl_primitive!(i32); -impl_primitive!(u64); -impl_primitive!(i64); +impl_primitive!(u32, next_u32); +impl_primitive!(i32, next_u32); +impl_primitive!(u64, next_u64); +impl_primitive!(i64, next_u64); impl_primitive!(u128); impl_primitive!(i128); impl_primitive!(usize); From 3cde98b511e03771883d5e34bee0368b668fdc17 Mon Sep 17 00:00:00 2001 From: Hanna Kruppe Date: Sun, 31 May 2026 17:49:17 +0200 Subject: [PATCH 2/2] core::random: from_le_bytes for reproducibility --- library/core/src/random.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/random.rs b/library/core/src/random.rs index 99517a3da6415..d69b09b432f3e 100644 --- a/library/core/src/random.rs +++ b/library/core/src/random.rs @@ -22,7 +22,7 @@ pub trait RandomSource { fn next_u32(&mut self) -> u32 { let mut buf = [0; size_of::()]; self.fill_bytes(&mut buf); - u32::from_ne_bytes(buf) + u32::from_le_bytes(buf) } /// Returns a random 64-bit integer. @@ -34,7 +34,7 @@ pub trait RandomSource { fn next_u64(&mut self) -> u64 { let mut buf = [0; size_of::()]; self.fill_bytes(&mut buf); - u64::from_ne_bytes(buf) + u64::from_le_bytes(buf) } } @@ -71,7 +71,7 @@ macro_rules! impl_primitive { fn sample(&self, source: &mut (impl RandomSource + ?Sized)) -> $t { let mut bytes = (0 as $t).to_ne_bytes(); source.fill_bytes(&mut bytes); - <$t>::from_ne_bytes(bytes) + <$t>::from_le_bytes(bytes) } } };