diff --git a/Cargo.toml b/Cargo.toml index afd13391d9d..a1bfb5b5efa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,10 @@ std_rng = ["rand_chacha", "rand_hc"] # Option: enable SmallRng small_rng = [] +# Option: for rustc ≥ 1.51, enable generating random arrays of any size +# using min-const-generics +min_const_gen = [] + [workspace] members = [ "rand_core", diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 652f52a1831..911d1604aae 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -279,6 +279,12 @@ where /// * Arrays (up to 32 elements): each element is generated sequentially; /// see also [`Rng::fill`] which supports arbitrary array length for integer /// types and tends to be faster for `u32` and smaller types. +/// When using `rustc` ≥ 1.51, enable the `min_const_gen` feature to support +/// arrays larger than 32 elements. +/// Note that [`Rng::fill`] and `Standard`'s array support are *not* equivalent: +/// the former is optimised for integer types (using fewer RNG calls for +/// element types smaller than the RNG word size), while the latter supports +/// any element type supported by `Standard`. /// * `Option` first generates a `bool`, and if true generates and returns /// `Some(value)` where `value: T`, otherwise returning `None`. /// diff --git a/src/distributions/other.rs b/src/distributions/other.rs index f62fe59aa73..16b352ac382 100644 --- a/src/distributions/other.rs +++ b/src/distributions/other.rs @@ -16,6 +16,9 @@ use crate::Rng; #[cfg(feature = "serde1")] use serde::{Serialize, Deserialize}; +#[cfg(feature = "min_const_gen")] +use std::mem::{self, MaybeUninit}; + // ----- Sampling distributions ----- @@ -156,6 +159,23 @@ tuple_impl! {A, B, C, D, E, F, G, H, I, J} tuple_impl! {A, B, C, D, E, F, G, H, I, J, K} tuple_impl! {A, B, C, D, E, F, G, H, I, J, K, L} +#[cfg(feature = "min_const_gen")] +impl Distribution<[T; N]> for Standard +where Standard: Distribution +{ + #[inline] + fn sample(&self, _rng: &mut R) -> [T; N] { + let mut buff: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; + + for elem in &mut buff { + *elem = MaybeUninit::new(_rng.gen()); + } + + unsafe { mem::transmute_copy::<_, _>(&buff) } + } +} + +#[cfg(not(feature = "min_const_gen"))] macro_rules! array_impl { // recursive, given at least one type parameter: {$n:expr, $t:ident, $($ts:ident,)*} => { @@ -176,6 +196,7 @@ macro_rules! array_impl { }; } +#[cfg(not(feature = "min_const_gen"))] array_impl! {32, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,} impl Distribution> for Standard diff --git a/src/rng.rs b/src/rng.rs index abb6750d26d..f555a68586d 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -71,6 +71,8 @@ pub trait Rng: RngCore { /// The `rng.gen()` method is able to generate arrays (up to 32 elements) /// and tuples (up to 12 elements), so long as all element types can be /// generated. + /// When using `rustc` ≥ 1.51, enable the `min_const_gen` feature to support + /// arrays larger than 32 elements. /// /// For arrays of integers, especially for those with small element types /// (< 64 bit), it will likely be faster to instead use [`Rng::fill`]. @@ -394,6 +396,16 @@ impl_fill!(i8, i16, i32, i64, isize,); #[cfg(not(target_os = "emscripten"))] impl_fill!(i128); +#[cfg(feature = "min_const_gen")] +impl Fill for [T; N] +where [T]: Fill +{ + fn try_fill(&mut self, rng: &mut R) -> Result<(), Error> { + self[..].try_fill(rng) + } +} + +#[cfg(not(feature = "min_const_gen"))] macro_rules! impl_fill_arrays { ($n:expr,) => {}; ($n:expr, $N:ident) => { @@ -413,8 +425,10 @@ macro_rules! impl_fill_arrays { impl_fill_arrays!(!div $n / 2, $($NN,)*); }; } +#[cfg(not(feature = "min_const_gen"))] #[rustfmt::skip] impl_fill_arrays!(32, N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,); +#[cfg(not(feature = "min_const_gen"))] impl_fill_arrays!(!div 4096, N,N,N,N,N,N,N,); #[cfg(test)]