From 14c9fd1e2cef090b48ce9e08ca68edc50a4347dc Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 30 Jul 2025 08:26:41 +0100 Subject: [PATCH 1/5] Remove impl_fill_each: Fill for [bool], [char], [f32], [f64] These impls contradict the doc that fn fill is implemented for types which may be reinterpreted as [u8]. --- src/rng.rs | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index c502e1ba47..8af856be75 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -368,25 +368,6 @@ pub trait Fill { fn fill(&mut self, rng: &mut R); } -macro_rules! impl_fill_each { - () => {}; - ($t:ty) => { - impl Fill for [$t] { - fn fill(&mut self, rng: &mut R) { - for elt in self.iter_mut() { - *elt = rng.random(); - } - } - } - }; - ($t:ty, $($tt:ty,)*) => { - impl_fill_each!($t); - impl_fill_each!($($tt,)*); - }; -} - -impl_fill_each!(bool, char, f32, f64,); - impl Fill for [u8] { fn fill(&mut self, rng: &mut R) { rng.fill_bytes(self) @@ -525,12 +506,6 @@ mod test { rng.fill(&mut warray[..]); assert_eq!(array[0], warray[0].0); assert_eq!(array[1], warray[1].0); - - // Check equivalence for generated floats - let mut array = [0f32; 2]; - rng.fill(&mut array); - let arr2: [f32; 2] = rng.random(); - assert_eq!(array, arr2); } #[test] From e236d01c01674b14929140ca7e486620cf0a8930 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 30 Jul 2025 08:32:38 +0100 Subject: [PATCH 2/5] Let trait Fill be implemented for element types, not sliceable types --- src/lib.rs | 4 +-- src/rng.rs | 78 +++++++++++++++++++++++++----------------------------- 2 files changed, 38 insertions(+), 44 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9187c9cc16..d39c6d0e63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -293,8 +293,8 @@ pub fn random_ratio(numerator: u32, denominator: u32) -> bool { #[cfg(feature = "thread_rng")] #[inline] #[track_caller] -pub fn fill(dest: &mut T) { - dest.fill(&mut rng()) +pub fn fill(dest: &mut [T]) { + Fill::fill_slice(dest, &mut rng()) } #[cfg(test)] diff --git a/src/rng.rs b/src/rng.rs index 8af856be75..1fa186bcb4 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -311,8 +311,8 @@ pub trait Rng: RngCore { /// /// [`fill_bytes`]: RngCore::fill_bytes #[track_caller] - fn fill(&mut self, dest: &mut T) { - dest.fill(self) + fn fill(&mut self, dest: &mut [T]) { + Fill::fill_slice(dest, self) } /// Alias for [`Rng::random`]. @@ -356,21 +356,24 @@ pub trait Rng: RngCore { impl Rng for R {} -/// Types which may be filled with random data +/// Support filling a slice with random data /// -/// This trait allows arrays to be efficiently filled with random data. +/// This trait allows slices of "plain data" types to be efficiently filled +/// with random data. /// /// Implementations are expected to be portable across machines unless /// clearly documented otherwise (see the /// [Chapter on Portability](https://rust-random.github.io/book/portability.html)). -pub trait Fill { - /// Fill self with random data - fn fill(&mut self, rng: &mut R); +/// The implementations provided achieve this by byte-swapping on big-endian +/// machines. +pub trait Fill: Sized { + /// Fill this with random data + fn fill_slice(this: &mut [Self], rng: &mut R); } -impl Fill for [u8] { - fn fill(&mut self, rng: &mut R) { - rng.fill_bytes(self) +impl Fill for u8 { + fn fill_slice(this: &mut [Self], rng: &mut R) { + rng.fill_bytes(this) } } @@ -387,48 +390,48 @@ macro_rules! impl_fill { // Force caller to wrap with an `unsafe` block __unsafe(); - impl Fill for [$t] { - fn fill(&mut self, rng: &mut R) { - if self.len() > 0 { - let size = mem::size_of_val(self); + impl Fill for $t { + fn fill_slice(this: &mut [Self], rng: &mut R) { + if this.len() > 0 { + let size = mem::size_of_val(this); rng.fill_bytes( - // SAFETY: `self` non-null and valid for reads and writes within its `size` - // bytes. `self` meets the alignment requirements of `&mut [u8]`. - // The contents of `self` are initialized. Both `[u8]` and `[$t]` are valid + // SAFETY: `this` non-null and valid for reads and writes within its `size` + // bytes. `this` meets the alignment requirements of `&mut [u8]`. + // The contents of `this` are initialized. Both `[u8]` and `[$t]` are valid // for all bit-patterns of their contents (note that the SAFETY requirement - // on callers of this macro). `self` is not borrowed. + // on callers of this macro). `this` is not borrowed. unsafe { - slice::from_raw_parts_mut(self.as_mut_ptr() + slice::from_raw_parts_mut(this.as_mut_ptr() as *mut u8, size ) } ); - for x in self { + for x in this { *x = x.to_le(); } } } } - impl Fill for [Wrapping<$t>] { - fn fill(&mut self, rng: &mut R) { - if self.len() > 0 { - let size = self.len() * mem::size_of::<$t>(); + impl Fill for Wrapping<$t> { + fn fill_slice(this: &mut [Self], rng: &mut R) { + if this.len() > 0 { + let size = this.len() * mem::size_of::<$t>(); rng.fill_bytes( - // SAFETY: `self` non-null and valid for reads and writes within its `size` - // bytes. `self` meets the alignment requirements of `&mut [u8]`. - // The contents of `self` are initialized. Both `[u8]` and `[$t]` are valid + // SAFETY: `this` non-null and valid for reads and writes within its `size` + // bytes. `this` meets the alignment requirements of `&mut [u8]`. + // The contents of `this` are initialized. Both `[u8]` and `[$t]` are valid // for all bit-patterns of their contents (note that the SAFETY requirement - // on callers of this macro). `self` is not borrowed. + // on callers of this macro). `this` is not borrowed. unsafe { - slice::from_raw_parts_mut(self.as_mut_ptr() + slice::from_raw_parts_mut(this.as_mut_ptr() as *mut u8, size ) } ); - for x in self { + for x in this { *x = Wrapping(x.0.to_le()); } } @@ -448,15 +451,6 @@ const _: () = unsafe { impl_fill!(u16, u32, u64, u128,) }; // SAFETY: All bit patterns of `[u8; size_of::<$t>()]` represent values of `i*`. const _: () = unsafe { impl_fill!(i8, i16, i32, i64, i128,) }; -impl Fill for [T; N] -where - [T]: Fill, -{ - fn fill(&mut self, rng: &mut R) { - <[T] as Fill>::fill(self, rng) - } -} - #[cfg(test)] mod test { use super::*; @@ -491,19 +485,19 @@ mod test { // Convert to byte sequence and back to u64; byte-swap twice if BE. let mut array = [0u64; 2]; - rng.fill(&mut array[..]); + rng.fill(&mut array); assert_eq!(array, [x, x]); assert_eq!(rng.next_u64(), x); // Convert to bytes then u32 in LE order let mut array = [0u32; 2]; - rng.fill(&mut array[..]); + rng.fill(&mut array); assert_eq!(array, [x as u32, (x >> 32) as u32]); assert_eq!(rng.next_u32(), x as u32); // Check equivalence using wrapped arrays let mut warray = [Wrapping(0u32); 2]; - rng.fill(&mut warray[..]); + rng.fill(&mut warray); assert_eq!(array[0], warray[0].0); assert_eq!(array[1], warray[1].0); } From 2f7f83ef87dbdb3345c3e7994ac30853901b8f9d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 7 Aug 2025 08:45:53 +0100 Subject: [PATCH 3/5] Test that Fill may be implemented for externally-defined types --- tests/fill.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/fill.rs diff --git a/tests/fill.rs b/tests/fill.rs new file mode 100644 index 0000000000..3178723b81 --- /dev/null +++ b/tests/fill.rs @@ -0,0 +1,19 @@ +// Copyright 2025 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] + +use rand::{Fill, Rng}; + +// Test that Fill may be implemented for externally-defined types +struct MyInt(i32); +impl Fill for MyInt { + fn fill_slice(this: &mut [Self], rng: &mut R) { + todo!() + } +} From 8b6d4f9f624a9746f12a99e2a90bb3d04432762e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 7 Aug 2025 08:47:47 +0100 Subject: [PATCH 4/5] Revise impl_fill macro --- src/rng.rs | 73 +++++++++++++++++++++++------------------------------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index 1fa186bcb4..66af038627 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -386,56 +386,45 @@ const unsafe fn __unsafe() {} /// All bit patterns of `[u8; size_of::<$t>()]` must represent values of `$t`. macro_rules! impl_fill { () => {}; + (to_le! plain $x:ident) => { + $x.to_le() + }; + (to_le! wrapping $x:ident) => { + Wrapping($x.0.to_le()) + }; + (fill_slice! $t:ty, $to_le:tt) => { + fn fill_slice(this: &mut [Self], rng: &mut R) { + if this.len() > 0 { + let size = mem::size_of_val(this); + rng.fill_bytes( + // SAFETY: `this` non-null and valid for reads and writes within its `size` + // bytes. `this` meets the alignment requirements of `&mut [u8]`. + // The contents of `this` are initialized. Both `[u8]` and `[$t]` are valid + // for all bit-patterns of their contents (note that the SAFETY requirement + // on callers of this macro). `this` is not borrowed. + unsafe { + slice::from_raw_parts_mut(this.as_mut_ptr() + as *mut u8, + size + ) + } + ); + for x in this { + *x = impl_fill!(to_le! $to_le x); + } + } + } + }; ($t:ty) => {{ // Force caller to wrap with an `unsafe` block __unsafe(); impl Fill for $t { - fn fill_slice(this: &mut [Self], rng: &mut R) { - if this.len() > 0 { - let size = mem::size_of_val(this); - rng.fill_bytes( - // SAFETY: `this` non-null and valid for reads and writes within its `size` - // bytes. `this` meets the alignment requirements of `&mut [u8]`. - // The contents of `this` are initialized. Both `[u8]` and `[$t]` are valid - // for all bit-patterns of their contents (note that the SAFETY requirement - // on callers of this macro). `this` is not borrowed. - unsafe { - slice::from_raw_parts_mut(this.as_mut_ptr() - as *mut u8, - size - ) - } - ); - for x in this { - *x = x.to_le(); - } - } - } + impl_fill!(fill_slice! $t, plain); } impl Fill for Wrapping<$t> { - fn fill_slice(this: &mut [Self], rng: &mut R) { - if this.len() > 0 { - let size = this.len() * mem::size_of::<$t>(); - rng.fill_bytes( - // SAFETY: `this` non-null and valid for reads and writes within its `size` - // bytes. `this` meets the alignment requirements of `&mut [u8]`. - // The contents of `this` are initialized. Both `[u8]` and `[$t]` are valid - // for all bit-patterns of their contents (note that the SAFETY requirement - // on callers of this macro). `this` is not borrowed. - unsafe { - slice::from_raw_parts_mut(this.as_mut_ptr() - as *mut u8, - size - ) - } - ); - for x in this { - *x = Wrapping(x.0.to_le()); - } - } - } + impl_fill!(fill_slice! $t, wrapping); }} }; ($t:ty, $($tt:ty,)*) => {{ From a1faaebdf375def34313fdfe5a344cd33c0fe392 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 7 Aug 2025 08:49:28 +0100 Subject: [PATCH 5/5] CHANGELOG --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 474e2c159f..e2c979cde7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,12 @@ A [separate changelog is kept for rand_core](rand_core/CHANGELOG.md). You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.html) useful. ## [Unreleased] -## Additions +### Additions - Pub export `Xoshiro128PlusPlus`, `Xoshiro256PlusPlus` prngs (#1649) +### Changes +- Let `Fill` be implemented for element types, not sliceable types (#1652) + ## [0.9.2 — 2025-07-20] ### Deprecated - Deprecate `rand::rngs::mock` module and `StepRng` generator (#1634)