rand_core: add blanket impl of TryRngCore for RngCore (#1499)

This PR removes the hacky `impl_try_rng_from_rng_core` and
`impl_try_crypto_rng_from_crypto_rng` macros and replaces them with
blanket impls of `TryRngCore` for `RngCore` and `TryCryptoRng` for
`CryptoRng`.

This change means that `TryRngCore`/`TryCryptoRng` no longer can have
blanket impls for `&mut R` and `Box<R>`. But I think it should be
tolerable since most users will be using `RngCore`/`CryptoRng`, which
have blanket impl for `DerefMut` (it covers both `&mut R` and `Box<R>`).
This commit is contained in:
Artyom Pavlov
2024-10-09 13:27:07 +03:00
committed by GitHub
parent 9c2787de73
commit 0fba9401c4
17 changed files with 75 additions and 184 deletions
+4 -4
View File
@@ -20,7 +20,7 @@ criterion_main!(benches);
pub fn bench(c: &mut Criterion) {
c.bench_function("seq_slice_choose_1_of_100", |b| {
let mut rng = Pcg32::from_rng(thread_rng());
let mut rng = Pcg32::from_rng(&mut thread_rng());
let mut buf = [0i32; 100];
rng.fill(&mut buf);
let x = black_box(&mut buf);
@@ -32,7 +32,7 @@ pub fn bench(c: &mut Criterion) {
for (amount, len) in lens {
let name = format!("seq_slice_choose_multiple_{}_of_{}", amount, len);
c.bench_function(name.as_str(), |b| {
let mut rng = Pcg32::from_rng(thread_rng());
let mut rng = Pcg32::from_rng(&mut thread_rng());
let mut buf = [0i32; 1000];
rng.fill(&mut buf);
let x = black_box(&buf[..len]);
@@ -53,7 +53,7 @@ pub fn bench(c: &mut Criterion) {
}
c.bench_function("seq_iter_choose_multiple_10_of_100", |b| {
let mut rng = Pcg32::from_rng(thread_rng());
let mut rng = Pcg32::from_rng(&mut thread_rng());
let mut buf = [0i32; 100];
rng.fill(&mut buf);
let x = black_box(&buf);
@@ -61,7 +61,7 @@ pub fn bench(c: &mut Criterion) {
});
c.bench_function("seq_iter_choose_multiple_fill_10_of_100", |b| {
let mut rng = Pcg32::from_rng(thread_rng());
let mut rng = Pcg32::from_rng(&mut thread_rng());
let mut buf = [0i32; 100];
rng.fill(&mut buf);
let x = black_box(&buf);
+1 -1
View File
@@ -20,7 +20,7 @@ criterion_main!(benches);
pub fn bench(c: &mut Criterion) {
c.bench_function("seq_shuffle_100", |b| {
let mut rng = Pcg32::from_rng(thread_rng());
let mut rng = Pcg32::from_rng(&mut thread_rng());
let mut buf = [0i32; 100];
rng.fill(&mut buf);
let x = black_box(&mut buf);
+2 -2
View File
@@ -23,7 +23,7 @@ const N_RESAMPLES: usize = 10_000;
macro_rules! sample {
($R:ty, $T:ty, $U:ty, $g:expr) => {
$g.bench_function(BenchmarkId::new(stringify!($R), "single"), |b| {
let mut rng = <$R>::from_rng(thread_rng());
let mut rng = <$R>::from_rng(&mut thread_rng());
let x = rng.random::<$U>();
let bits = (<$T>::BITS / 2);
let mask = (1 as $U).wrapping_neg() >> bits;
@@ -35,7 +35,7 @@ macro_rules! sample {
});
$g.bench_function(BenchmarkId::new(stringify!($R), "distr"), |b| {
let mut rng = <$R>::from_rng(thread_rng());
let mut rng = <$R>::from_rng(&mut thread_rng());
let x = rng.random::<$U>();
let bits = (<$T>::BITS / 2);
let mask = (1 as $U).wrapping_neg() >> bits;
+2 -2
View File
@@ -27,7 +27,7 @@ const N_RESAMPLES: usize = 10_000;
macro_rules! single_random {
($R:ty, $T:ty, $g:expr) => {
$g.bench_function(BenchmarkId::new(stringify!($T), stringify!($R)), |b| {
let mut rng = <$R>::from_rng(thread_rng());
let mut rng = <$R>::from_rng(&mut thread_rng());
let (mut low, mut high);
loop {
low = <$T>::from_bits(rng.random());
@@ -63,7 +63,7 @@ fn single_random(c: &mut Criterion) {
macro_rules! distr_random {
($R:ty, $T:ty, $g:expr) => {
$g.bench_function(BenchmarkId::new(stringify!($T), stringify!($R)), |b| {
let mut rng = <$R>::from_rng(thread_rng());
let mut rng = <$R>::from_rng(&mut thread_rng());
let dist = loop {
let low = <$T>::from_bits(rng.random());
let high = <$T>::from_bits(rng.random());
+1 -1
View File
@@ -53,7 +53,7 @@ pub fn bench(c: &mut Criterion) {
c.bench_function(name.as_str(), |b| {
let length = black_box(length);
let amount = black_box(amount);
let mut rng = SmallRng::from_rng(thread_rng());
let mut rng = SmallRng::from_rng(&mut thread_rng());
b.iter(|| sample_weighted(&mut rng, length, |idx| (1 + (idx % 100)) as u32, amount))
});
}
-2
View File
@@ -257,8 +257,6 @@ macro_rules! chacha_impl {
impl CryptoRng for $ChaChaXRng {}
rand_core::impl_try_crypto_rng_from_crypto_rng!($ChaChaXRng);
impl From<$ChaChaXCore> for $ChaChaXRng {
fn from(core: $ChaChaXCore) -> Self {
$ChaChaXRng {
-88
View File
@@ -1,88 +0,0 @@
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use crate::{CryptoRng, RngCore, TryCryptoRng, TryRngCore};
impl<'a, R: RngCore + ?Sized> RngCore for &'a mut R {
#[inline(always)]
fn next_u32(&mut self) -> u32 {
R::next_u32(self)
}
#[inline(always)]
fn next_u64(&mut self) -> u64 {
R::next_u64(self)
}
#[inline(always)]
fn fill_bytes(&mut self, dst: &mut [u8]) {
R::fill_bytes(self, dst)
}
}
impl<'a, R: CryptoRng + ?Sized> CryptoRng for &'a mut R {}
impl<'a, R: TryRngCore + ?Sized> TryRngCore for &'a mut R {
type Error = R::Error;
#[inline(always)]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
R::try_next_u32(self)
}
#[inline(always)]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
R::try_next_u64(self)
}
#[inline(always)]
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
R::try_fill_bytes(self, dst)
}
}
impl<'a, R: TryCryptoRng + ?Sized> TryCryptoRng for &'a mut R {}
#[cfg(feature = "alloc")]
impl<R: RngCore + ?Sized> RngCore for Box<R> {
#[inline(always)]
fn next_u32(&mut self) -> u32 {
R::next_u32(self)
}
#[inline(always)]
fn next_u64(&mut self) -> u64 {
R::next_u64(self)
}
#[inline(always)]
fn fill_bytes(&mut self, dest: &mut [u8]) {
R::fill_bytes(self, dest)
}
}
#[cfg(feature = "alloc")]
impl<R: CryptoRng + ?Sized> CryptoRng for Box<R> {}
#[cfg(feature = "alloc")]
impl<R: TryRngCore + ?Sized> TryRngCore for Box<R> {
type Error = R::Error;
#[inline(always)]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
R::try_next_u32(self)
}
#[inline(always)]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
R::try_next_u64(self)
}
#[inline(always)]
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
R::try_fill_bytes(self, dst)
}
}
#[cfg(feature = "alloc")]
impl<R: TryCryptoRng + ?Sized> TryCryptoRng for Box<R> {}
+4 -4
View File
@@ -250,12 +250,12 @@ impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng<R> {
}
#[inline(always)]
fn from_rng(rng: impl RngCore) -> Self {
fn from_rng(rng: &mut impl RngCore) -> Self {
Self::new(R::from_rng(rng))
}
#[inline(always)]
fn try_from_rng<S: TryRngCore>(rng: S) -> Result<Self, S::Error> {
fn try_from_rng<S: TryRngCore>(rng: &mut S) -> Result<Self, S::Error> {
R::try_from_rng(rng).map(Self::new)
}
}
@@ -415,12 +415,12 @@ impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng64<R> {
}
#[inline(always)]
fn from_rng(rng: impl RngCore) -> Self {
fn from_rng(rng: &mut impl RngCore) -> Self {
Self::new(R::from_rng(rng))
}
#[inline(always)]
fn try_from_rng<S: TryRngCore>(rng: S) -> Result<Self, S::Error> {
fn try_from_rng<S: TryRngCore>(rng: &mut S) -> Result<Self, S::Error> {
R::try_from_rng(rng).map(Self::new)
}
}
-54
View File
@@ -160,60 +160,6 @@ pub fn next_u64_via_fill<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
u64::from_le_bytes(buf)
}
/// Implement [`TryRngCore`][crate::TryRngCore] for a type implementing [`RngCore`].
///
/// Ideally, `rand_core` would define blanket impls for this, but they conflict with blanket impls
/// for `&mut R` and `Box<R>`, so until specialziation is stabilized, implementer crates
/// have to implement `TryRngCore` directly.
#[macro_export]
macro_rules! impl_try_rng_from_rng_core {
($t:ty) => {
impl $crate::TryRngCore for $t {
type Error = core::convert::Infallible;
#[inline]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
Ok($crate::RngCore::next_u32(self))
}
#[inline]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
Ok($crate::RngCore::next_u64(self))
}
#[inline]
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
$crate::RngCore::fill_bytes(self, dst);
Ok(())
}
}
};
}
/// Implement [`TryCryptoRng`] and [`TryRngCore`] for a type implementing [`CryptoRng`].
///
/// Ideally, `rand_core` would define blanket impls for this, but they conflict with blanket impls
/// for `&mut R` and `Box<R>`, so until specialziation is stabilized, implementer crates
/// have to implement `TryRngCore` and `TryCryptoRng` directly.
///
/// [`TryCryptoRng`]: crate::TryCryptoRng
/// [`TryRngCore`]: crate::TryRngCore
/// [`CryptoRng`]: crate::CryptoRng
#[macro_export]
macro_rules! impl_try_crypto_rng_from_crypto_rng {
($t:ty) => {
$crate::impl_try_rng_from_rng_core!($t);
impl $crate::TryCryptoRng for $t {}
/// Check at compile time that `$t` implements `CryptoRng`
const _: () = {
const fn check_crypto_rng_impl<T: $crate::CryptoRng>() {}
check_crypto_rng_impl::<$t>();
};
};
}
#[cfg(test)]
mod test {
use super::*;
+60 -13
View File
@@ -40,9 +40,8 @@ extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use core::fmt;
use core::{fmt, ops::DerefMut};
mod blanket_impls;
pub mod block;
pub mod impls;
pub mod le;
@@ -83,9 +82,9 @@ pub use os::OsRng;
/// in this trait directly, then use the helper functions from the
/// [`impls`] module to implement the other methods.
///
/// Implementors of [`RngCore`] SHOULD also implement the [`TryRngCore`]
/// trait with the `Error` associated type being equal to [`Infallible`].
/// It can be done using the [`impl_try_rng_from_rng_core!`] macro.
/// Note that implementors of [`RngCore`] also automatically implement
/// the [`TryRngCore`] trait with the `Error` associated type being
/// equal to [`Infallible`].
///
/// It is recommended that implementations also implement:
///
@@ -125,8 +124,6 @@ pub use os::OsRng;
/// impls::fill_bytes_via_next(self, dst)
/// }
/// }
///
/// rand_core::impl_try_rng_from_rng_core!(CountingRng);
/// ```
///
/// [`rand::Rng`]: https://docs.rs/rand/latest/rand/trait.Rng.html
@@ -162,13 +159,29 @@ pub trait RngCore {
fn fill_bytes(&mut self, dst: &mut [u8]);
}
impl<T: DerefMut> RngCore for T
where
T::Target: RngCore,
{
#[inline]
fn next_u32(&mut self) -> u32 {
self.deref_mut().next_u32()
}
#[inline]
fn next_u64(&mut self) -> u64 {
self.deref_mut().next_u64()
}
#[inline]
fn fill_bytes(&mut self, dst: &mut [u8]) {
self.deref_mut().fill_bytes(dst);
}
}
/// A marker trait used to indicate that an [`RngCore`] implementation is
/// supposed to be cryptographically secure.
///
/// Implementors of [`CryptoRng`] SHOULD also implement the [`TryCryptoRng`]
/// trait with the `Error` associated type being equal to [`Infallible`].
/// It can be done using the [`impl_try_crypto_rng_from_crypto_rng!`] macro.
///
/// *Cryptographically secure generators*, also known as *CSPRNGs*, should
/// satisfy an additional properties over other generators: given the first
/// *k* bits of an algorithm's output
@@ -187,10 +200,15 @@ pub trait RngCore {
/// Note also that use of a `CryptoRng` does not protect against other
/// weaknesses such as seeding from a weak entropy source or leaking state.
///
/// Note that implementors of [`CryptoRng`] also automatically implement
/// the [`TryCryptoRng`] trait.
///
/// [`BlockRngCore`]: block::BlockRngCore
/// [`Infallible`]: core::convert::Infallible
pub trait CryptoRng: RngCore {}
impl<T: DerefMut> CryptoRng for T where T::Target: CryptoRng {}
/// A potentially fallible version of [`RngCore`].
///
/// This trait is primarily used for IO-based generators such as [`OsRng`].
@@ -233,26 +251,54 @@ pub trait TryRngCore {
}
}
// Note that, unfortunately, this blanket impl prevents us from implementing
// `TryRngCore` for types which can be dereferenced to `TryRngCore`, i.e. `TryRngCore`
// will not be automatically implemented for `&mut R`, `Box<R>`, etc.
impl<R: RngCore> TryRngCore for R {
type Error = core::convert::Infallible;
#[inline]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
Ok(self.next_u32())
}
#[inline]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
Ok(self.next_u64())
}
#[inline]
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
self.fill_bytes(dst);
Ok(())
}
}
/// A marker trait used to indicate that a [`TryRngCore`] implementation is
/// supposed to be cryptographically secure.
///
/// See [`CryptoRng`] docs for more information about cryptographically secure generators.
pub trait TryCryptoRng: TryRngCore {}
impl<R: CryptoRng> TryCryptoRng for R {}
/// Wrapper around [`TryRngCore`] implementation which implements [`RngCore`]
/// by panicking on potential errors.
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
pub struct UnwrapErr<R: TryRngCore>(pub R);
impl<R: TryRngCore> RngCore for UnwrapErr<R> {
#[inline]
fn next_u32(&mut self) -> u32 {
self.0.try_next_u32().unwrap()
}
#[inline]
fn next_u64(&mut self) -> u64 {
self.0.try_next_u64().unwrap()
}
#[inline]
fn fill_bytes(&mut self, dst: &mut [u8]) {
self.0.try_fill_bytes(dst).unwrap()
}
@@ -416,7 +462,7 @@ pub trait SeedableRng: Sized {
/// (in prior versions this was not required).
///
/// [`rand`]: https://docs.rs/rand
fn from_rng(mut rng: impl RngCore) -> Self {
fn from_rng(rng: &mut impl RngCore) -> Self {
let mut seed = Self::Seed::default();
rng.fill_bytes(seed.as_mut());
Self::from_seed(seed)
@@ -425,7 +471,7 @@ pub trait SeedableRng: Sized {
/// Create a new PRNG seeded from a potentially fallible `Rng`.
///
/// See [`from_rng`][SeedableRng::from_rng] docs for more information.
fn try_from_rng<R: TryRngCore>(mut rng: R) -> Result<Self, R::Error> {
fn try_from_rng<R: TryRngCore>(rng: &mut R) -> Result<Self, R::Error> {
let mut seed = Self::Seed::default();
rng.try_fill_bytes(seed.as_mut())?;
Ok(Self::from_seed(seed))
@@ -492,6 +538,7 @@ pub struct RngReadAdapter<'a, R: TryRngCore + ?Sized> {
#[cfg(feature = "std")]
impl<R: TryRngCore + ?Sized> std::io::Read for RngReadAdapter<'_, R> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
self.inner.try_fill_bytes(buf).map_err(|err| {
std::io::Error::new(std::io::ErrorKind::Other, std::format!("RNG error: {err}"))
+1 -1
View File
@@ -14,7 +14,7 @@
//!
//! ```
//! use rand::prelude::*;
//! # let mut r = StdRng::from_rng(thread_rng());
//! # let mut r = StdRng::from_rng(&mut thread_rng());
//! # let _: f32 = r.random();
//! ```
-2
View File
@@ -75,8 +75,6 @@ impl RngCore for StepRng {
}
}
rand_core::impl_try_rng_from_rng_core!(StepRng);
#[cfg(test)]
mod tests {
#[cfg(any(feature = "alloc", feature = "serde"))]
-2
View File
@@ -113,8 +113,6 @@ impl RngCore for SmallRng {
}
}
rand_core::impl_try_rng_from_rng_core!(SmallRng);
impl SmallRng {
/// Construct an instance seeded from the thread-local RNG
///
-2
View File
@@ -91,8 +91,6 @@ impl SeedableRng for StdRng {
impl CryptoRng for StdRng {}
rand_core::impl_try_crypto_rng_from_crypto_rng!(StdRng);
#[cfg(test)]
mod test {
use crate::rngs::StdRng;
-2
View File
@@ -170,8 +170,6 @@ impl RngCore for ThreadRng {
impl CryptoRng for ThreadRng {}
rand_core::impl_try_crypto_rng_from_crypto_rng!(ThreadRng);
#[cfg(test)]
mod test {
#[test]
-2
View File
@@ -99,8 +99,6 @@ impl RngCore for Xoshiro128PlusPlus {
}
}
rand_core::impl_try_rng_from_rng_core!(Xoshiro128PlusPlus);
#[cfg(test)]
mod tests {
use super::Xoshiro128PlusPlus;
-2
View File
@@ -101,8 +101,6 @@ impl RngCore for Xoshiro256PlusPlus {
}
}
rand_core::impl_try_rng_from_rng_core!(Xoshiro256PlusPlus);
#[cfg(test)]
mod tests {
use super::Xoshiro256PlusPlus;