From 752fdbfbf93bf827033c478fc974cadce9893f4d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 08:13:32 +0000 Subject: [PATCH 01/16] impl TryRngCore for R: DerefMut where Target: TryRngCore --- src/lib.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a7323bc7..3696be2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,23 +186,25 @@ 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`, etc. -impl TryRngCore for R { - type Error = core::convert::Infallible; +impl TryRngCore for R +where + R::Target: TryRngCore, +{ + type Error = ::Error; #[inline] fn try_next_u32(&mut self) -> Result { - Ok(self.next_u32()) + self.deref_mut().try_next_u32() } #[inline] fn try_next_u64(&mut self) -> Result { - Ok(self.next_u64()) + self.deref_mut().try_next_u64() } #[inline] fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> { - self.fill_bytes(dst); - Ok(()) + self.deref_mut().try_fill_bytes(dst) } } @@ -224,7 +226,7 @@ impl TryRngCore for R { /// [`OsRng`]: https://docs.rs/rand/latest/rand/rngs/struct.OsRng.html pub trait TryCryptoRng: TryRngCore {} -impl TryCryptoRng for R {} +impl TryCryptoRng for R where R::Target: TryCryptoRng {} /// Wrapper around [`TryRngCore`] implementation which implements [`RngCore`] /// by panicking on potential errors. From 87c0cd8bdd19727a903ec91ace2d69ca523667e7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 08:18:04 +0000 Subject: [PATCH 02/16] Remove UnwrapMut, moving fn re to UnwrapErr --- src/lib.rs | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3696be2e..823841a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -176,11 +176,6 @@ pub trait TryRngCore { { UnwrapErr(self) } - - /// Wrap RNG with the [`UnwrapMut`] wrapper. - fn unwrap_mut(&mut self) -> UnwrapMut<'_, Self> { - UnwrapMut(self) - } } // Note that, unfortunately, this blanket impl prevents us from implementing @@ -252,45 +247,21 @@ impl RngCore for UnwrapErr { impl CryptoRng for UnwrapErr {} -/// Wrapper around [`TryRngCore`] implementation which implements [`RngCore`] -/// by panicking on potential errors. -#[derive(Debug, Eq, PartialEq, Hash)] -pub struct UnwrapMut<'r, R: TryRngCore + ?Sized>(pub &'r mut R); - -impl<'r, R: TryRngCore + ?Sized> UnwrapMut<'r, R> { +impl<'r, R: TryRngCore + ?Sized> UnwrapErr<&'r mut R> { /// Reborrow with a new lifetime /// /// Rust allows references like `&T` or `&mut T` to be "reborrowed" through /// coercion: essentially, the pointer is copied under a new, shorter, lifetime. /// Until rfcs#1403 lands, reborrows on user types require a method call. #[inline(always)] - pub fn re<'b>(&'b mut self) -> UnwrapMut<'b, R> + pub fn re<'b>(&'b mut self) -> UnwrapErr<&'b mut R> where 'r: 'b, { - UnwrapMut(self.0) - } -} - -impl RngCore for UnwrapMut<'_, 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() + UnwrapErr(self.0) } } -impl CryptoRng for UnwrapMut<'_, R> {} - /// A random number generator that can be explicitly seeded. /// /// This trait encapsulates the low-level functionality common to all From 25c4f5d0fae464284b17564fe04d960837f839ad Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 08:20:19 +0000 Subject: [PATCH 03/16] impl TryRngCore for UnwrapErr --- src/lib.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 823841a3..e2cbbbed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ clippy::undocumented_unsafe_blocks )] -use core::{fmt, ops::DerefMut}; +use core::{convert::Infallible, fmt, ops::DerefMut}; pub mod block; pub mod utils; @@ -75,7 +75,6 @@ mod word; /// [`fill_bytes`]: RngCore::fill_bytes /// [`next_u32`]: RngCore::next_u32 /// [`next_u64`]: RngCore::next_u64 -/// [`Infallible`]: core::convert::Infallible pub trait RngCore { /// Return the next random `u32`. fn next_u32(&mut self) -> u32; @@ -150,8 +149,7 @@ impl CryptoRng for T where T::Target: CryptoRng {} /// fallible IO-based generators such as [`OsRng`]. /// /// All implementations of [`RngCore`] automatically support this `TryRngCore` -/// trait, using [`Infallible`][core::convert::Infallible] as the associated -/// `Error` type. +/// trait, using [`Infallible`] as the associated `Error` type. /// /// An implementation of this trait may be made compatible with code requiring /// an [`RngCore`] through [`TryRngCore::unwrap_err`]. The resulting RNG will @@ -228,24 +226,26 @@ impl TryCryptoRng for R where R::Target: TryCryptoRng {} #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] pub struct UnwrapErr(pub R); -impl RngCore for UnwrapErr { +impl TryRngCore for UnwrapErr { + type Error = Infallible; + #[inline] - fn next_u32(&mut self) -> u32 { - self.0.try_next_u32().unwrap() + fn try_next_u32(&mut self) -> Result { + Ok(self.0.try_next_u32().unwrap()) } #[inline] - fn next_u64(&mut self) -> u64 { - self.0.try_next_u64().unwrap() + fn try_next_u64(&mut self) -> Result { + Ok(self.0.try_next_u64().unwrap()) } #[inline] - fn fill_bytes(&mut self, dst: &mut [u8]) { - self.0.try_fill_bytes(dst).unwrap() + fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> { + Ok(self.0.try_fill_bytes(dst).unwrap()) } } -impl CryptoRng for UnwrapErr {} +impl TryCryptoRng for UnwrapErr {} impl<'r, R: TryRngCore + ?Sized> UnwrapErr<&'r mut R> { /// Reborrow with a new lifetime @@ -605,7 +605,7 @@ mod test { struct FourRng; impl TryRngCore for FourRng { - type Error = core::convert::Infallible; + type Error = Infallible; fn try_next_u32(&mut self) -> Result { Ok(4) } From ea88cf9ae6ce1c223101331b234130ef6e3dc7d5 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 08:15:04 +0000 Subject: [PATCH 04/16] Let RngCore: TryRngCore --- src/lib.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e2cbbbed..94ad8aeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,7 +75,7 @@ mod word; /// [`fill_bytes`]: RngCore::fill_bytes /// [`next_u32`]: RngCore::next_u32 /// [`next_u64`]: RngCore::next_u64 -pub trait RngCore { +pub trait RngCore: TryRngCore { /// Return the next random `u32`. fn next_u32(&mut self) -> u32; @@ -91,23 +91,26 @@ pub trait RngCore { fn fill_bytes(&mut self, dst: &mut [u8]); } -impl RngCore for T -where - T::Target: RngCore, -{ +impl> RngCore for R { #[inline] fn next_u32(&mut self) -> u32 { - self.deref_mut().next_u32() + match self.try_next_u32() { + Ok(x) => x, + } } #[inline] fn next_u64(&mut self) -> u64 { - self.deref_mut().next_u64() + match self.try_next_u64() { + Ok(x) => x, + } } #[inline] fn fill_bytes(&mut self, dst: &mut [u8]) { - self.deref_mut().fill_bytes(dst); + match self.try_fill_bytes(dst) { + Ok(()) => (), + } } } @@ -139,9 +142,9 @@ where /// prior output values. This property is not required by `CryptoRng`. /// /// [`OsRng`]: https://docs.rs/rand/latest/rand/rngs/struct.OsRng.html -pub trait CryptoRng: RngCore {} +pub trait CryptoRng: RngCore + TryCryptoRng {} -impl CryptoRng for T where T::Target: CryptoRng {} +impl CryptoRng for R {} /// A potentially fallible variant of [`RngCore`] /// From c7fd184a2b1512a60ed338ab414bc1c3c12fb294 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 08:27:56 +0000 Subject: [PATCH 05/16] Fix tests --- src/block.rs | 19 +++++++++++-------- src/lib.rs | 24 ++++++++++++------------ src/utils.rs | 19 +++++++++++-------- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/block.rs b/src/block.rs index d8b6e12a..190a98c8 100644 --- a/src/block.rs +++ b/src/block.rs @@ -14,7 +14,8 @@ //! # Example //! //! ``` -//! use rand_core::{RngCore, SeedableRng}; +//! use core::convert::Infallible; +//! use rand_core::{RngCore, SeedableRng, TryRngCore}; //! use rand_core::block::{Generator, BlockRng}; //! //! struct MyRngCore { @@ -45,20 +46,22 @@ //! } //! } //! -//! impl RngCore for MyRng { +//! impl TryRngCore for MyRng { +//! type Error = Infallible; +//! //! #[inline] -//! fn next_u32(&mut self) -> u32 { -//! self.0.next_word() +//! fn try_next_u32(&mut self) -> Result { +//! Ok(self.0.next_word()) //! } //! //! #[inline] -//! fn next_u64(&mut self) -> u64 { -//! self.0.next_u64_from_u32() +//! fn try_next_u64(&mut self) -> Result { +//! Ok(self.0.next_u64_from_u32()) //! } //! //! #[inline] -//! fn fill_bytes(&mut self, bytes: &mut [u8]) { -//! self.0.fill_bytes(bytes) +//! fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Infallible> { +//! Ok(self.0.fill_bytes(bytes)) //! } //! } //! diff --git a/src/lib.rs b/src/lib.rs index 94ad8aeb..5c1cca9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -519,19 +519,21 @@ mod test { // A stub RNG. struct SomeRng; - impl RngCore for SomeRng { - fn next_u32(&mut self) -> u32 { + impl TryRngCore for SomeRng { + type Error = Infallible; + + fn try_next_u32(&mut self) -> Result { unimplemented!() } - fn next_u64(&mut self) -> u64 { + fn try_next_u64(&mut self) -> Result { unimplemented!() } - fn fill_bytes(&mut self, _: &mut [u8]) { + fn try_fill_bytes(&mut self, _dst: &mut [u8]) -> Result<(), Self::Error> { unimplemented!() } } - impl CryptoRng for SomeRng {} + impl TryCryptoRng for SomeRng {} #[test] fn dyn_rngcore_to_tryrngcore() { @@ -571,15 +573,14 @@ mod test { #[test] fn dyn_unwrap_mut_tryrngcore() { - // Illustrates the need for `+ ?Sized` bound in - // `impl RngCore for UnwrapMut<'_, R>`. + // Illustrates that UnwrapMut may be used over &mut R where R: TryRngCore fn third_party_api(_rng: &mut impl RngCore) -> bool { true } fn my_api(rng: &mut (impl TryRngCore + ?Sized)) -> bool { - let mut infallible_rng = rng.unwrap_mut(); + let mut infallible_rng = rng.unwrap_err(); third_party_api(&mut infallible_rng) } @@ -588,15 +589,14 @@ mod test { #[test] fn dyn_unwrap_mut_trycryptorng() { - // Illustrates the need for `+ ?Sized` bound in - // `impl CryptoRng for UnwrapMut<'_, R>`. + // Crypto variant of the above fn third_party_api(_rng: &mut impl CryptoRng) -> bool { true } fn my_api(rng: &mut (impl TryCryptoRng + ?Sized)) -> bool { - let mut infallible_rng = rng.unwrap_mut(); + let mut infallible_rng = rng.unwrap_err(); third_party_api(&mut infallible_rng) } @@ -621,7 +621,7 @@ mod test { } let mut rng = FourRng; - let mut rng = rng.unwrap_mut(); + let mut rng = (&mut rng).unwrap_err(); assert_eq!(rng.next_u32(), 4); { diff --git a/src/utils.rs b/src/utils.rs index 4fcd294c..046d4255 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -40,7 +40,8 @@ //! from M.E. O'Neill's blog post //! [Does It Beat the Minimal Standard?](https://www.pcg-random.org/posts/does-it-beat-the-minimal-standard.html). //! ``` -//! use rand_core::{RngCore, SeedableRng, utils}; +//! use core::convert::Infallible; +//! use rand_core::{RngCore, SeedableRng, TryRngCore, utils}; //! //! pub struct Mcg128(u128); //! @@ -54,21 +55,23 @@ //! } //! } //! -//! impl RngCore for Mcg128 { +//! impl TryRngCore for Mcg128 { +//! type Error = Infallible; +//! //! #[inline] -//! fn next_u32(&mut self) -> u32 { -//! (self.next_u64() >> 32) as u32 +//! fn try_next_u32(&mut self) -> Result { +//! Ok((self.next_u64() >> 32) as u32) //! } //! //! #[inline] -//! fn next_u64(&mut self) -> u64 { +//! fn try_next_u64(&mut self) -> Result { //! self.0 = self.0.wrapping_mul(0x0fc94e3bf4e9ab32866458cd56f5e605); -//! (self.0 >> 64) as u64 +//! Ok((self.0 >> 64) as u64) //! } //! //! #[inline] -//! fn fill_bytes(&mut self, dst: &mut [u8]) { -//! utils::fill_bytes_via_next_word(dst, || self.next_u64()); +//! fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Infallible> { +//! Ok(utils::fill_bytes_via_next_word(dst, || self.next_u64())) //! } //! } //! # From a2a35c305e142873e22a1a6cdfd86b459b17b072 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 11:25:29 +0000 Subject: [PATCH 06/16] Doc: RngCore and TryRngCore traits --- src/lib.rs | 151 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 97 insertions(+), 54 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5c1cca9d..5c2aceb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,58 +19,26 @@ pub mod block; pub mod utils; mod word; -/// Implementation-level interface for RNGs +/// Trait for infallible random number generators /// -/// This trait encapsulates the low-level functionality common to all -/// generators, and is the "back end", to be implemented by generators. -/// End users should normally use the [`rand::Rng`] trait -/// which is automatically implemented for every type implementing `RngCore`. -/// -/// Three different methods for generating random data are provided since the -/// optimal implementation of each is dependent on the type of generator. There -/// is no required relationship between the output of each; e.g. many -/// implementations of [`fill_bytes`] consume a whole number of `u32` or `u64` -/// values and drop any remaining unused bytes. The same can happen with the -/// [`next_u32`] and [`next_u64`] methods, implementations may discard some -/// random bits for efficiency. -/// -/// # Properties of a generator -/// -/// Implementers should produce bits uniformly. Pathological RNGs (e.g. constant -/// or counting generators which rarely change some bits) may cause issues in -/// consumers of random data, for example dead-locks in rejection samplers and -/// obviously non-random output (e.g. a counting generator may result in -/// apparently-constant output from a uniform-ranged distribution). +/// `RngCore` is a sub-trait of [`TryRngCore`] for infallible generators. /// -/// Algorithmic generators implementing [`SeedableRng`] should normally have -/// *portable, reproducible* output, i.e. fix Endianness when converting values -/// to avoid platform differences, and avoid making any changes which affect -/// output (except by communicating that the release has breaking changes). +/// # Requirements /// -/// # Implementing `RngCore` +/// See [`TryRngCore`#Requirements] which also apply here. /// -/// Typically an RNG will implement only one of the methods available -/// in this trait directly, then use the helper functions from the -/// [`utils`] module to implement the other methods. +/// # Usage /// -/// Note that implementors of [`RngCore`] also automatically implement -/// the [`TryRngCore`] trait with the `Error` associated type being -/// equal to [`Infallible`]. +/// The [`rand`] crate provides higher level functionality, for example +/// generation of floating-point values, uniform ranged sampling and shuffling +/// sequences. In particular, [`rand::Rng`] is an extension trait over `RngCore` +/// providing many of the methods one might expect to be able to use on an RNG. /// -/// It is recommended that implementations also implement: +/// # Implementing `RngCore` /// -/// - `Debug` with a custom implementation which *does not* print any internal -/// state (at least, [`CryptoRng`]s should not risk leaking state through -/// `Debug`). -/// - `Serialize` and `Deserialize` (from Serde), preferably making Serde -/// support optional at the crate level in PRNG libs. -/// - `Clone`, if possible. -/// - *never* implement `Copy` (accidental copies may cause repeated values). -/// - *do not* implement `Default` for pseudorandom generators, but instead -/// implement [`SeedableRng`], to guide users towards proper seeding. -/// External / hardware RNGs can choose to implement `Default`. -/// - `Eq` and `PartialEq` could be implemented, but are probably not useful. +/// Implement [`TryRngCore`] with type Error = [Infallible][]. /// +/// [`rand`]: https://docs.rs/rand/ /// [`rand::Rng`]: https://docs.rs/rand/latest/rand/trait.Rng.html /// [`fill_bytes`]: RngCore::fill_bytes /// [`next_u32`]: RngCore::next_u32 @@ -146,28 +114,103 @@ pub trait CryptoRng: RngCore + TryCryptoRng {} impl CryptoRng for R {} -/// A potentially fallible variant of [`RngCore`] +/// Base trait for random number generators and random data sources /// -/// This trait is a generalization of [`RngCore`] to support potentially- -/// fallible IO-based generators such as [`OsRng`]. +/// This trait provides a base interface designed to support efficient usage of +/// (`u32`, `u64`) word generators, block generators and random data sources. +/// There is no required relationship between the output of each method or any +/// requirement to use all generated random bits; for example an implementation +/// of [`try_fill_bytes`](Self::try_fill_bytes) may discard some generated bytes +/// to avoid storing a partially used word or block. /// -/// All implementations of [`RngCore`] automatically support this `TryRngCore` -/// trait, using [`Infallible`] as the associated `Error` type. +/// # Requirements /// -/// An implementation of this trait may be made compatible with code requiring -/// an [`RngCore`] through [`TryRngCore::unwrap_err`]. The resulting RNG will -/// panic in case the underlying fallible RNG yields an error. +/// ### Quality and length /// -/// [`OsRng`]: https://docs.rs/rand/latest/rand/rngs/struct.OsRng.html +/// Implementions should produce bits uniformly: each output value should be +/// equally likely, without observable patterns in successive outputs or +/// between the output streams of multiple instances of an implementation using +/// different seeds or streams (where supported by the implementation). +/// +/// Pathological implementations (e.g. constant or counting generators which +/// rarely change some bits) may cause issues in consumers of random data, for +/// example dead-locks in rejection samplers and obviously non-random output +/// (e.g. a counting generator may result in apparently-constant output from a +/// uniform-ranged distribution). +/// +/// Cryptographically unpredictable output is not a requirement of this trait, +/// but is a requirement of [`TryCryptoRng`]. +/// +/// In practice, most implementations are pseudo-random number generators with a +/// finite *period* or *cycle length*, and (among non-cryptographic PRNGs) +/// statistical anomalies may appear long before a cycle occurs. An +/// implementation should ensure its period is sufficiently long that no +/// anomalies are likely to appear in usage and/or document its limitations. +/// [The Rust Rand Book: Quality](https://rust-random.github.io/book/guide-rngs.html#quality). +/// +/// ### Reproducibility +/// +/// Algorithmic generators implementing [`SeedableRng`] should normally have +/// *portable, reproducible* output, i.e. fix Endianness when converting values +/// to avoid platform differences, and avoid making any changes which affect +/// output (except by communicating that the release has breaking changes). +/// See also [The Rust Rand Book: Reproducibility](https://rust-random.github.io/book/crate-reprod.html). +/// +/// # Usage +/// +/// Often, usage of the infallible trait [`RngCore`] or its extension trait +/// [`rand::Rng`] is preferred to direct usage of `TryRngCore`. +/// +/// Many implementations of `TryRngCore` (those with +/// type Error = [Infallible][]) already implement [`RngCore`]; in +/// other cases [`Self::unwrap_err`] may be used to obtain an implementation of +/// [`RngCore`]. +/// +/// # Implementing `TryRngCore` +/// +/// Most algorithmic generators (i.e. pseudo-random number generators or PRNGs) +/// never fail; in this case type `Error` should be [`Infallible`]; in this case +/// trait `RngCore` is implemented automatically. Cycling is not considered an +/// error. +/// +/// Small PRNGs often yield either `u32` or `u64` natively. +/// Module [`crate::utils`] provides utilities to help implement other methods. +/// +/// Byte sources may implement [`try_fill_bytes`](Self::try_fill_bytes) +/// natively. +/// Module [`crate::utils`] provides utilities to help implement other methods. +/// +/// Block generators (which produce `[u32; N]` or `[u64; N]` for some fixed `N`) +/// should make use of the [`crate::block`] module. +/// +/// With regards to other traits: +/// +/// - **Do not** implement [`Default`] for seedable pseudorandom generators, +/// though the trait may be implemented for stateless interfaces and +/// auto-seeding generators. +/// - **Do** implement [`SeedableRng`] for seedable pseudorandom generators. See +/// [Reproducibility](#reproducibility) above. +/// - Implement [`Clone`] for non-cryptographic PRNGs but consider not doing so +/// for cryptographic generators to avoid the risk of key-stream duplication. +/// - **Do not** implement [`Copy`] since accidental copies may cause repeated +/// values. +/// - Implement [`Debug`](core::fmt::Debug), except that cryptographic PRNGs +/// should use a custom implementation which avoids leaking internal state (or +/// the subset of this derived from the key). +/// - [`Eq`] and [`PartialEq`] could be implemented, but are probably not useful. +/// +/// [`rand::Rng`]: https://docs.rs/rand/latest/rand/trait.Rng.html pub trait TryRngCore { /// The type returned in the event of a RNG error. + /// + /// Use type [`Infallible`] for infallible implementations. type Error: fmt::Debug + fmt::Display; /// Return the next random `u32`. fn try_next_u32(&mut self) -> Result; /// Return the next random `u64`. fn try_next_u64(&mut self) -> Result; - /// Fill `dest` entirely with random data. + /// Fill `dst` entirely with random data. fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error>; /// Wrap RNG with the [`UnwrapErr`] wrapper. From 20003bd705797782798786e462e8a90c8c8c2269 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 11:26:10 +0000 Subject: [PATCH 07/16] Doc: crypto traits --- src/lib.rs | 52 +++++++++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5c2aceb7..23f1c535 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,32 +84,10 @@ impl> RngCore for R { /// A marker trait over [`RngCore`] for securely unpredictable RNGs /// -/// This marker trait indicates that the implementing generator is intended, -/// when correctly seeded and protected from side-channel attacks such as a -/// leaking of state, to be a cryptographically secure generator. This trait is -/// provided as a tool to aid review of cryptographic code, but does not by -/// itself guarantee suitability for cryptographic applications. -/// -/// Implementors of `CryptoRng` automatically implement the [`TryCryptoRng`] -/// trait. -/// -/// Implementors of `CryptoRng` should only implement [`Default`] if the -/// `default()` instances are themselves secure generators: for example if the -/// implementing type is a stateless interface over a secure external generator -/// (like [`OsRng`]) or if the `default()` instance uses a strong, fresh seed. -/// -/// Formally, a CSPRNG (Cryptographically Secure Pseudo-Random Number Generator) -/// should satisfy an additional property over other generators: assuming that -/// the generator has been appropriately seeded and has unknown state, then -/// given the first *k* bits of an algorithm's output -/// sequence, it should not be possible using polynomial-time algorithms to -/// predict the next bit with probability significantly greater than 50%. -/// -/// An optional property of CSPRNGs is backtracking resistance: if the CSPRNG's -/// state is revealed, it will not be computationally-feasible to reconstruct -/// prior output values. This property is not required by `CryptoRng`. -/// -/// [`OsRng`]: https://docs.rs/rand/latest/rand/rngs/struct.OsRng.html +/// This is just the sum trait [RngCore][] + [TryCryptoRng]. In +/// generic bounds, usage of `RngCore + TryCryptoRng` is equivalent, however +/// sum traits are not ([yet](https://github.com/rust-lang/rfcs/issues/2035)) +/// [`dyn`-safe](https://quinedot.github.io/rust-learning/dyn-safety.html). pub trait CryptoRng: RngCore + TryCryptoRng {} impl CryptoRng for R {} @@ -249,20 +227,28 @@ where /// A marker trait over [`TryRngCore`] for securely unpredictable RNGs /// -/// This trait is like [`CryptoRng`] but for the trait [`TryRngCore`]. -/// /// This marker trait indicates that the implementing generator is intended, /// when correctly seeded and protected from side-channel attacks such as a /// leaking of state, to be a cryptographically secure generator. This trait is /// provided as a tool to aid review of cryptographic code, but does not by /// itself guarantee suitability for cryptographic applications. /// -/// Implementors of `TryCryptoRng` should only implement [`Default`] if the -/// `default()` instances are themselves secure generators: for example if the -/// implementing type is a stateless interface over a secure external generator -/// (like [`OsRng`]) or if the `default()` instance uses a strong, fresh seed. +/// Formally, a CSPRNG (Cryptographically Secure Pseudo-Random Number Generator) +/// should satisfy an additional property over other generators: assuming that +/// the generator has been appropriately seeded and has unknown state, then +/// given the first *k* bits of an algorithm's output +/// sequence, it should not be possible using polynomial-time algorithms to +/// predict the next bit with probability significantly greater than 50%. +/// +/// An optional property of CSPRNGs is backtracking resistance: if the CSPRNG's +/// state is revealed, it will not be computationally-feasible to reconstruct +/// prior output values. This property is not required by `CryptoRng`. +/// +/// Implementors of `TryCryptoRng` should only implement [`Default`] if a +/// default-constructed instance is itself a secure generator, for example +/// [`getrandom::SysRng`] which is a stateless interface. /// -/// [`OsRng`]: https://docs.rs/rand/latest/rand/rngs/struct.OsRng.html +/// [`getrandom::SysRng`]: https://docs.rs/getrandom/latest/getrandom/struct.SysRng.html pub trait TryCryptoRng: TryRngCore {} impl TryCryptoRng for R where R::Target: TryCryptoRng {} From 4a467b589349efd4442ba1ae8f8fe53e1ddac96a Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 11:38:41 +0000 Subject: [PATCH 08/16] Doc: block module --- src/block.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/block.rs b/src/block.rs index 190a98c8..ecf2fca7 100644 --- a/src/block.rs +++ b/src/block.rs @@ -6,8 +6,8 @@ //! //! The struct [`BlockRng`] wraps such a [`Generator`] together with an output //! buffer and implements several methods (e.g. [`BlockRng::next_word`]) to -//! assist in the implementation of [`RngCore`]. Note that (unlike in earlier -//! versions of `rand_core`) [`BlockRng`] itself does not implement [`RngCore`] +//! assist in the implementation of [`TryRngCore`]. Note that (unlike in earlier +//! versions of `rand_core`) [`BlockRng`] itself does not implement [`TryRngCore`] //! since in practice we found it was always beneficial to use a wrapper type //! over [`BlockRng`]. //! @@ -65,7 +65,7 @@ //! } //! } //! -//! // And if applicable: impl CryptoRng for MyRng {} +//! // And if applicable: impl TryCryptoRng for MyRng {} //! //! let mut rng = MyRng::seed_from_u64(0); //! println!("First value: {}", rng.next_u32()); @@ -77,10 +77,10 @@ //! The [`Generator`] trait supports usage of [`rand::rngs::ReseedingRng`]. //! This requires that [`SeedableRng`] be implemented on the "core" generator. //! Additionally, it may be useful to implement [`CryptoGenerator`]. -//! (This is in addition to any implementations on an [`RngCore`] type.) +//! (This is in addition to any implementations on an [`TryRngCore`] type.) //! //! [`Generator`]: crate::block::Generator -//! [`RngCore`]: crate::RngCore +//! [`TryRngCore`]: crate::TryRngCore //! [`SeedableRng`]: crate::SeedableRng //! [`rand::rngs::ReseedingRng`]: https://docs.rs/rand/latest/rand/rngs/struct.ReseedingRng.html @@ -118,7 +118,7 @@ pub trait Generator { /// `#[cfg(test)]` attribute to ensure that mock "crypto" generators cannot be /// used in production. /// -/// See [`CryptoRng`](crate::CryptoRng) docs for more information. +/// See [`TryCryptoRng`](crate::TryCryptoRng) docs for more information. pub trait CryptoGenerator: Generator {} /// RNG functionality for a block [`Generator`] From 635c64e1be5e35f4847e6598b983f12fabc648b1 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 11:41:11 +0000 Subject: [PATCH 09/16] Doc: utils module --- src/utils.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index 046d4255..02a248b2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -11,22 +11,22 @@ //! to/from byte sequences, and since its purpose is reproducibility, //! non-reproducible sources (e.g. `OsRng`) need not bother with it. //! -//! ## Implementing [`RngCore`] +//! ## Implementing [`TryRngCore`] //! -//! Usually an implementation of [`RngCore`] will implement one of the three +//! Usually an implementation of [`TryRngCore`] will implement one of the three //! methods over its internal source. The following helpers are provided for //! the remaining implementations. //! -//! **`fn next_u32`:** +//! **`fn try_next_u32`:** //! - `self.next_u64() as u32` //! - `(self.next_u64() >> 32) as u32` //! - [next_word_via_fill][](self) //! -//! **`fn next_u64`:** +//! **`fn try_next_u64`:** //! - [next_u64_via_u32][](self) //! - [next_word_via_fill][](self) //! -//! **`fn fill_bytes`:** +//! **`fn try_fill_bytes`:** //! - [fill_bytes_via_next_word][](self, dest) //! //! ## Implementing [`SeedableRng`] @@ -84,9 +84,9 @@ //! ``` use crate::RngCore; -#[allow(unused)] -use crate::SeedableRng; pub use crate::word::Word; +#[allow(unused)] +use crate::{SeedableRng, TryRngCore}; /// Implement `next_u64` via `next_u32`, little-endian order. #[inline] @@ -99,7 +99,7 @@ pub fn next_u64_via_u32(rng: &mut R) -> u64 { /// Fill `dst` with bytes using `next_word` /// -/// This may be used to implement [`RngCore::fill_bytes`] over `next_u32` or +/// This may be used to implement `fill_bytes` over `next_u32` or /// `next_u64`. Words are used in order of generation. The last word may be /// partially discarded. #[inline] From ae26d433bf18b10a0379d26b9747b98f250de769 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 11:44:05 +0000 Subject: [PATCH 10/16] Let next_u64_via_u32 and next_word_via_fill use closures --- src/utils.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index 02a248b2..e5a8f523 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -83,17 +83,16 @@ //! # assert_eq!(buf, [154, 23, 43, 68, 75]); //! ``` -use crate::RngCore; pub use crate::word::Word; #[allow(unused)] use crate::{SeedableRng, TryRngCore}; -/// Implement `next_u64` via `next_u32`, little-endian order. +/// Generate a `u64` using `next_u32`, little-endian order. #[inline] -pub fn next_u64_via_u32(rng: &mut R) -> u64 { +pub fn next_u64_via_u32(mut next_u32: impl FnMut() -> u32) -> u64 { // Use LE; we explicitly generate one value before the next. - let x = u64::from(rng.next_u32()); - let y = u64::from(rng.next_u32()); + let x = u64::from(next_u32()); + let y = u64::from(next_u32()); (y << 32) | x } @@ -116,12 +115,10 @@ pub fn fill_bytes_via_next_word(dst: &mut [u8], mut next_word: impl FnM } } -/// Yield a word using [`RngCore::fill_bytes`] -/// -/// This may be used to implement `next_u32` or `next_u64`. -pub fn next_word_via_fill(rng: &mut R) -> W { +/// Generate a `u32` or `u64` word using `fill_bytes` +pub fn next_word_via_fill(mut fill_bytes: impl FnMut(&mut [u8])) -> W { let mut buf: W::Bytes = Default::default(); - rng.fill_bytes(buf.as_mut()); + fill_bytes(buf.as_mut()); W::from_le_bytes(buf) } From b0fabb0f2a6b88e8b8596b8125be55cfebf71007 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 12:18:31 +0000 Subject: [PATCH 11/16] Shut Clippy up --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 23f1c535..cd3e5ae1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -273,7 +273,8 @@ impl TryRngCore for UnwrapErr { #[inline] fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> { - Ok(self.0.try_fill_bytes(dst).unwrap()) + self.0.try_fill_bytes(dst).unwrap(); + Ok(()) } } From b339a5c62b892d9b9ae6002ab69d1cef66824c84 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 12 Jan 2026 12:20:37 +0000 Subject: [PATCH 12/16] CHAEGLOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc74d2a4..3240601d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Replace `le::fill_bytes_via_next` with `utils::fill_bytes_via_next_word` ([#38]) - Replace `le::next_u32_via_fill` and `le::next_u64_via_fill` with `utils::next_word_via_fill` ([#38]) - Replace `le::read_u32_into` and `le::read_u64_into` with `utils::read_words` ([#38]) +- Let `RngCore: TryRngCore` ([#45]) +- Remove `UnwrapMut` ([#45]) +- Let `utils::{next_u64_via_u32, next_word_via_fill}` take a closure instead of `R: RngCore` ([#45]) ### Other - Changed repository from [rust-random/rand] to [rust-random/core]. @@ -43,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#35]: https://github.com/rust-random/rand-core/pull/35 [#36]: https://github.com/rust-random/rand-core/pull/36 [#38]: https://github.com/rust-random/rand-core/pull/38 +[#45]: https://github.com/rust-random/rand-core/pull/45 [rust-random/rand]: https://github.com/rust-random/rand [rust-random/core]: https://github.com/rust-random/core From 269a63e83a5da0776fbb859f2d8ed5db08d365a7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 13 Jan 2026 08:54:53 +0000 Subject: [PATCH 13/16] Doc: review updates --- CHANGELOG.md | 2 +- src/lib.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3240601d..5aa48d68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Replace `le::fill_bytes_via_next` with `utils::fill_bytes_via_next_word` ([#38]) - Replace `le::next_u32_via_fill` and `le::next_u64_via_fill` with `utils::next_word_via_fill` ([#38]) - Replace `le::read_u32_into` and `le::read_u64_into` with `utils::read_words` ([#38]) -- Let `RngCore: TryRngCore` ([#45]) +- `RngCore` is now an extension trait of `TryRngCore` ([#45]) - Remove `UnwrapMut` ([#45]) - Let `utils::{next_u64_via_u32, next_word_via_fill}` take a closure instead of `R: RngCore` ([#45]) diff --git a/src/lib.rs b/src/lib.rs index cd3e5ae1..6aaa5b8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,7 @@ mod word; /// /// # Implementing `RngCore` /// -/// Implement [`TryRngCore`] with type Error = [Infallible][]. +/// Implement [`TryRngCore`] with type Error = [core::convert::Infallible][]. /// /// [`rand`]: https://docs.rs/rand/ /// [`rand::Rng`]: https://docs.rs/rand/latest/rand/trait.Rng.html @@ -124,6 +124,8 @@ impl CryptoRng for R {} /// statistical anomalies may appear long before a cycle occurs. An /// implementation should ensure its period is sufficiently long that no /// anomalies are likely to appear in usage and/or document its limitations. +/// +/// For more on PRNG quality and period, see /// [The Rust Rand Book: Quality](https://rust-random.github.io/book/guide-rngs.html#quality). /// /// ### Reproducibility @@ -181,7 +183,7 @@ impl CryptoRng for R {} pub trait TryRngCore { /// The type returned in the event of a RNG error. /// - /// Use type [`Infallible`] for infallible implementations. + /// Use type [`core::convert::Infallible`] for infallible implementations. type Error: fmt::Debug + fmt::Display; /// Return the next random `u32`. From 061219d1657f8f598b60d2fc3069b620b385341c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 13 Jan 2026 08:56:38 +0000 Subject: [PATCH 14/16] utils: use TryRngCore / Result --- src/utils.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index e5a8f523..c8504507 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -71,7 +71,7 @@ //! //! #[inline] //! fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Infallible> { -//! Ok(utils::fill_bytes_via_next_word(dst, || self.next_u64())) +//! utils::fill_bytes_via_next_word(dst, || self.try_next_u64()) //! } //! } //! # @@ -89,11 +89,11 @@ use crate::{SeedableRng, TryRngCore}; /// Generate a `u64` using `next_u32`, little-endian order. #[inline] -pub fn next_u64_via_u32(mut next_u32: impl FnMut() -> u32) -> u64 { +pub fn next_u64_via_u32(rng: &mut R) -> Result { // Use LE; we explicitly generate one value before the next. - let x = u64::from(next_u32()); - let y = u64::from(next_u32()); - (y << 32) | x + let x = u64::from(rng.try_next_u32()?); + let y = u64::from(rng.try_next_u32()?); + Ok((y << 32) | x) } /// Fill `dst` with bytes using `next_word` @@ -102,24 +102,28 @@ pub fn next_u64_via_u32(mut next_u32: impl FnMut() -> u32) -> u64 { /// `next_u64`. Words are used in order of generation. The last word may be /// partially discarded. #[inline] -pub fn fill_bytes_via_next_word(dst: &mut [u8], mut next_word: impl FnMut() -> W) { +pub fn fill_bytes_via_next_word( + dst: &mut [u8], + mut next_word: impl FnMut() -> Result, +) -> Result<(), E> { let mut chunks = dst.chunks_exact_mut(size_of::()); for chunk in &mut chunks { - let val = next_word(); + let val = next_word()?; chunk.copy_from_slice(val.to_le_bytes().as_ref()); } let rem = chunks.into_remainder(); if !rem.is_empty() { - let val = next_word().to_le_bytes(); + let val = next_word()?.to_le_bytes(); rem.copy_from_slice(&val.as_ref()[..rem.len()]); } + Ok(()) } /// Generate a `u32` or `u64` word using `fill_bytes` -pub fn next_word_via_fill(mut fill_bytes: impl FnMut(&mut [u8])) -> W { +pub fn next_word_via_fill(rng: &mut R) -> Result { let mut buf: W::Bytes = Default::default(); - fill_bytes(buf.as_mut()); - W::from_le_bytes(buf) + rng.try_fill_bytes(buf.as_mut())?; + Ok(W::from_le_bytes(buf)) } /// Reads an array of words from a byte slice From c6753881b15bd18fdefb8d0d5d5d0f20e54ebec5 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 14 Jan 2026 16:08:09 +0000 Subject: [PATCH 15/16] Review updates --- CHANGELOG.md | 2 +- src/lib.rs | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aa48d68..f35efd20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Replace `le::read_u32_into` and `le::read_u64_into` with `utils::read_words` ([#38]) - `RngCore` is now an extension trait of `TryRngCore` ([#45]) - Remove `UnwrapMut` ([#45]) -- Let `utils::{next_u64_via_u32, next_word_via_fill}` take a closure instead of `R: RngCore` ([#45]) +- Add error handling to `utils` functions over `TryRngCore` or via closure ([#45]) ### Other - Changed repository from [rust-random/rand] to [rust-random/core]. diff --git a/src/lib.rs b/src/lib.rs index 6aaa5b8c..13fc330f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,13 +84,11 @@ impl> RngCore for R { /// A marker trait over [`RngCore`] for securely unpredictable RNGs /// -/// This is just the sum trait [RngCore][] + [TryCryptoRng]. In -/// generic bounds, usage of `RngCore + TryCryptoRng` is equivalent, however -/// sum traits are not ([yet](https://github.com/rust-lang/rfcs/issues/2035)) -/// [`dyn`-safe](https://quinedot.github.io/rust-learning/dyn-safety.html). -pub trait CryptoRng: RngCore + TryCryptoRng {} +/// This is a convenient trait alias for `TryCryptoRng`. +/// This is also the trait sum of [RngCore][] + [TryCryptoRng]. +pub trait CryptoRng: TryCryptoRng {} -impl CryptoRng for R {} +impl> CryptoRng for R {} /// Base trait for random number generators and random data sources /// @@ -265,18 +263,23 @@ impl TryRngCore for UnwrapErr { #[inline] fn try_next_u32(&mut self) -> Result { - Ok(self.0.try_next_u32().unwrap()) + self.0 + .try_next_u32() + .map_err(|err| panic!("rand_core::UnwrapErr: failed to unwrap: {err}")) } #[inline] fn try_next_u64(&mut self) -> Result { - Ok(self.0.try_next_u64().unwrap()) + self.0 + .try_next_u64() + .map_err(|err| panic!("rand_core::UnwrapErr: failed to unwrap: {err}")) } #[inline] fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> { - self.0.try_fill_bytes(dst).unwrap(); - Ok(()) + self.0 + .try_fill_bytes(dst) + .map_err(|err| panic!("rand_core::UnwrapErr: failed to unwrap: {err}")) } } From d6ea763e549358fb0779614d30b2212740826867 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 14 Jan 2026 18:28:50 +0000 Subject: [PATCH 16/16] Doc tweak for CryptoRng --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 13fc330f..b4d077b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,10 +82,10 @@ impl> RngCore for R { } } -/// A marker trait over [`RngCore`] for securely unpredictable RNGs +/// A marker trait for securely unpredictable infallible RNGs /// -/// This is a convenient trait alias for `TryCryptoRng`. -/// This is also the trait sum of [RngCore][] + [TryCryptoRng]. +/// This is a convenient trait alias for [TryCryptoRng]. +/// It is equivalent to the trait sum [RngCore] + [TryCryptoRng]. pub trait CryptoRng: TryCryptoRng {} impl> CryptoRng for R {}