feat: support cloning random handles

Add OsRandom::try_clone for fallible duplication of the underlying
/dev/urandom file handle, and implement Clone as the infallible convenience
form. This gives independent call sites their own OsRandom values without
adding interior mutability or shared buffering to the core type.

The fallible method is documented as the preferred API when callers need to
handle a failed file-handle duplication. Clone mirrors Default by panicking only
for the convenience path.

Document both cloning forms with doctested examples and add a runnable clone
example that uses independent handles for IDs and string helpers.

Test Plan:
- cargo test
- cargo clippy
- cargo clippy --benches
- cargo clippy --tests
- cargo +nightly fmt

Refs: IDEAS.md ergonomics backlog
This commit is contained in:
2026-04-28 20:07:47 +02:00
parent 53fc84a270
commit 808e0482c3
2 changed files with 64 additions and 0 deletions
+49
View File
@@ -329,6 +329,35 @@ impl OsRandom {
self.devurandom.read_exact(buf)
}
/// Duplicates this handle to `/dev/urandom`.
///
/// The cloned value has its own [`File`] handle, so separate call sites can
/// own independent `OsRandom` values while reading from the same
/// operating-system randomness source.
///
/// # Errors
/// Returns any I/O error produced while duplicating the underlying file
/// handle.
///
/// # Examples
///
/// ```
/// # fn main() -> std::io::Result<()> {
/// let mut first = ez_urandom::OsRandom::try_new()?;
/// let mut second = first.try_clone()?;
///
/// let a = first.get_u64()?;
/// let b = second.get_u64()?;
/// # let _ = (a, b);
/// # Ok(())
/// # }
/// ```
pub fn try_clone(&self) -> io::Result<Self> {
Ok(Self {
devurandom: self.devurandom.try_clone()?,
})
}
os_random_get_integer_impls!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);
@@ -353,6 +382,26 @@ impl Default for OsRandom {
}
}
/// Duplicates the underlying `/dev/urandom` handle.
///
/// Prefer [`OsRandom::try_clone`] when the caller should decide how to handle an
/// I/O failure while duplicating the file handle.
///
/// # Examples
///
/// ```
/// let rng = ez_urandom::OsRandom::default();
/// let mut cloned = rng.clone();
/// let value = cloned.get_u32().expect("/dev/urandom read failed");
/// # let _ = value;
/// ```
impl Clone for OsRandom {
fn clone(&self) -> Self {
self.try_clone()
.expect("/dev/urandom handle should be cloneable")
}
}
/// Uniform sampling helpers.
///
/// Range methods use rejection sampling on top of the matching primitive