Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- `RawOsError` type alias [#739]
- `SysRng` behind new feature `sys_rng` [#751]
- `UnwrappingSysRng` behind new feature `sys_rng` [#754]

### Changed
- Use Edition 2024 and MSRV 1.85 [#749]

[#739]: https://github.com/rust-random/getrandom/pull/739
[#749]: https://github.com/rust-random/getrandom/pull/749
[#751]: https://github.com/rust-random/getrandom/pull/751
[#754]: https://github.com/rust-random/getrandom/pull/754

## [0.3.4] - 2025-10-14

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ mod sys_rng;
#[cfg(feature = "sys_rng")]
pub use rand_core;
#[cfg(feature = "sys_rng")]
pub use sys_rng::SysRng;
pub use sys_rng::{SysRng, UnwrappingSysRng};

pub use crate::error::{Error, RawOsError};

Expand Down
72 changes: 58 additions & 14 deletions src/sys_rng.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
use crate::Error;
use rand_core::{TryCryptoRng, TryRngCore};
use rand_core::{CryptoRng, RngCore, TryCryptoRng, TryRngCore};

/// A [`TryRngCore`] interface over the system's preferred random number source
/// A [`TryRngCore`] interface over the system's preferred random number source.
///
/// This is a zero-sized struct. It can be freely constructed with just `SysRng`.
///
/// This struct is also available as [`rand::rngs::SysRng`] when using [rand].
/// This struct is also available as [`rand::rngs::SysRng`] when using [`rand`].
///
/// If you don't care about potential (but extremely unlikely in practice) errors,
/// you can use [`UnwrappingSysRng`] instead.
///
/// # Usage example
///
/// `SysRng` implements [`TryRngCore`]:
/// ```
/// use getrandom::{rand_core::TryRngCore, SysRng};
///
/// # fn main() -> Result<(), getrandom::Error> {
/// let mut key = [0u8; 32];
/// SysRng.try_fill_bytes(&mut key).unwrap();
/// ```
///
/// Using it as an [`RngCore`] is possible using [`TryRngCore::unwrap_err`]:
/// ```
/// use getrandom::rand_core::{TryRngCore, RngCore};
/// use getrandom::SysRng;
/// SysRng.try_fill_bytes(&mut key)?;
///
/// let mut rng = SysRng.unwrap_err();
/// let random_u64 = rng.next_u64();
/// let x: u32 = SysRng.try_next_u32()?;
/// let y: u64 = SysRng.try_next_u64()?;
/// # Ok(()) }
/// ```
///
/// [rand]: https://crates.io/crates/rand
/// [`rand`]: https://crates.io/crates/rand
/// [`rand::rngs::SysRng`]: https://docs.rs/rand/latest/rand/rngs/struct.SysRng.html
/// [`RngCore`]: rand_core::RngCore
#[derive(Clone, Copy, Debug, Default)]
pub struct SysRng;

Expand All @@ -52,3 +50,49 @@ impl TryRngCore for SysRng {
}

impl TryCryptoRng for SysRng {}

/// A potentially-panicking [`RngCore`] interface over the system's preferred random number source.
///
/// This is a zero-sized struct. It can be freely constructed with just `UnwrappingSysRng`.
///
/// If possible, we recommend to use [`SysRng`] instead and to properly handle potential errors.
///
/// This struct is also available as [`rand::rngs::UnwrappingSysRng`] when using [`rand`].
///
/// # Usage example
///
/// `UnwrappingSysRng` implements [`RngCore`]:
/// ```
/// use getrandom::{rand_core::RngCore, UnwrappingSysRng};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: rustdoc formats this as:

Suggested change
/// use getrandom::{rand_core::RngCore, UnwrappingSysRng};
/// use getrandom::{UnwrappingSysRng, rand_core::RngCore};

///
/// let mut key = [0u8; 32];
/// UnwrappingSysRng.fill_bytes(&mut key);
///
/// let x: u32 = UnwrappingSysRng.next_u32();
/// let y: u64 = UnwrappingSysRng.next_u64();
/// ```
///
/// [`rand`]: https://crates.io/crates/rand
/// [`rand::rngs::UnwrappingSysRng`]: https://docs.rs/rand/latest/rand/rngs/struct.UnwrappingSysRng.html
/// [`RngCore`]: rand_core::RngCore
#[derive(Clone, Copy, Debug, Default)]
pub struct UnwrappingSysRng;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I'm not a fan of introducing another type which is just "SysRng but we unwrap all errors".

Currently, there are two ways to already do this with rand_core (which is entirely exported by this crate):

  • use getrandom::{SysRng, rand_core::TryRngCore};
    
    let mut rng = SysRng.unwrap_err();
  • use getrandom::{SysRng, rand_core::UnwrapErr};
    
    let mut rng = UnwrapErr(SysRng);

I don't think we need to add a third way, especially if (as you say) we recommend using SysRng instead of UnwrappingSysRng. Also, the two existing methods produce the same type (UnwrapErr<SysRng>), while this approach would add a new type.

I think we should instead update the docstring for SysRng to:

  • Have the initial usage examples use [TryRngCore] without unwraping the error (like you already have)
  • Include an additional example section on how someone might use this with RngCore. Would look something like:
## Use with [`RngCore`]

Unlike the use of [`TryRngCore`] above, the [`RngCore`] trait does not allow returning
an error type. While it is possible for [`SysRng`]/[`fill`] to fail, this is usually
uncommon on most platforms (see [link to main crate docs]). For this reason, users may
want to wrap [`SysRng`] with [`rand_core::UnwrapErr`], to automatically unwrap the unlikely
errors.

```rust
use getrandom::{SysRng, rand_core::{RngCore, UnwrapErr}};

let x: u32 = UnwrapErr(SysRng).next_u32();
let y: u64 = UnwrapErr(SysRng).next_u64();
```

This can also be done via the [`TryRngCore::unwrap_err()`] method as
`let mut rng = SysRng.unwrap_err();`

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't say we have "two ways" right now. They are doing the same thing, but expressed differently. Following your classification we would have a bunch of other "different" ways such as UnwrapErr::<SysRng>::default().

The main rationale for the additional type is that in practice the unwrapped variant probably will be used much more often than SysRng. While I think we should nudge users towards SysRng, I don't think we should unnecessarily annoy them.

The UnwrapErr(SysRng) is quite inconvenient to use because you have to import two separate items and it looks worse in code because it can contain both UnwrapErr(SysRng) and UnwrapErr<SysRng>. Granted, it's a small papercut, but a papercut nevertheless.

I don't think UnwrappingSysRng will cause any issues or confusion in practice. Users are likely to use it without even being aware about UnwrapErr existence and it would "just work". Scenarios in which type conflicts between UnwrappingSysRng and UnwrapErr(SysRng) could happen are highly unlikely in my opinion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main rationale for the additional type is that in practice the unwrapped variant probably will be used much more often than SysRng.

By users of rand, probably yes. But when it comes to direct users of getrandom I'm much less sure.

But even for rand, I don't know if we want this. Using UnwrapErr<SysRng> is not that unergonomic and I suspect will not be that common.

But I may be wrong. Which in this case is fine since there is no reason this could not be added in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect will not be that common.

It looks like OsRng has plenty of uses in practice and on the first glance most of them use it in the panicking mode.

Which in this case is fine since there is no reason this could not be added in the future.

It's likely to result in an annoying mix of UnwrapErr<SysRng> and UnwrappingSysRng in downstream crates. I think we either should introduce UnwrappingSysRng right away or stick with UnwrapErr<SysRng>.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of those uses are ancient, as evidenced by usage of OsRng::new().

Also, basically all of these uses are just constructing an instance of OsRng for local usage, so this would be replaced with SysRng.unwrap_err(), not UnwrapErr<SysRng>.

I wonder if we should make unwrap_err an inherent fn?


impl RngCore for UnwrappingSysRng {
#[inline]
fn next_u32(&mut self) -> u32 {
crate::u32().unwrap()
}

#[inline]
fn next_u64(&mut self) -> u64 {
crate::u64().unwrap()
}

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
crate::fill(dest).unwrap()
}
}

impl CryptoRng for UnwrappingSysRng {}
54 changes: 47 additions & 7 deletions tests/sys_rng.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,57 @@
#![cfg(feature = "sys_rng")]

use getrandom::SysRng;
use getrandom::rand_core::TryRngCore;
use core::array::from_fn;
use getrandom::{
SysRng, UnwrappingSysRng,
rand_core::{RngCore, TryRngCore},
};

const N: usize = 32;

#[test]
fn test_sys_rng() {
let x = SysRng.try_next_u64().unwrap();
let y = SysRng.try_next_u64().unwrap();
assert!(x != 0);
let x: [u64; N] = from_fn(|_| SysRng.try_next_u64().unwrap());
let y: [u64; N] = from_fn(|_| SysRng.try_next_u64().unwrap());
assert!(x.iter().all(|&val| val != 0));
assert!(y.iter().all(|&val| val != 0));
assert!(x != y);

let x: [u32; N] = from_fn(|_| SysRng.try_next_u32().unwrap());
let y: [u32; N] = from_fn(|_| SysRng.try_next_u32().unwrap());
assert!(x.iter().all(|&val| val != 0));
assert!(y.iter().all(|&val| val != 0));
assert!(x != y);

let mut x = [0u8; N];
SysRng.try_fill_bytes(&mut x).unwrap();
let mut y = [0u8; N];
SysRng.try_fill_bytes(&mut y).unwrap();

assert_ne!(x, [0; N]);
assert_ne!(y, [0; N]);
assert!(x != y);
}

#[test]
fn test_construction() {
assert!(SysRng.try_next_u64().unwrap() != 0);
fn test_unwrapping_sys_rng() {
let x: [u64; N] = from_fn(|_| UnwrappingSysRng.next_u64());
let y: [u64; N] = from_fn(|_| UnwrappingSysRng.next_u64());
assert!(x.iter().all(|&val| val != 0));
assert!(y.iter().all(|&val| val != 0));
assert!(x != y);

let x: [u32; N] = from_fn(|_| UnwrappingSysRng.next_u32());
let y: [u32; N] = from_fn(|_| UnwrappingSysRng.next_u32());
assert!(x.iter().all(|&val| val != 0));
assert!(y.iter().all(|&val| val != 0));
assert!(x != y);

let mut x = [0u8; N];
UnwrappingSysRng.fill_bytes(&mut x);
let mut y = [0u8; N];
UnwrappingSysRng.fill_bytes(&mut y);

assert_ne!(x, [0; N]);
assert_ne!(y, [0; N]);
assert!(x != y);
}