diff --git a/examples/clone.rs b/examples/clone.rs new file mode 100644 index 0000000..3df12ed --- /dev/null +++ b/examples/clone.rs @@ -0,0 +1,15 @@ +//! Run with: `cargo run --example clone` + +use ez_urandom::OsRandom; + +fn main() -> std::io::Result<()> { + let mut ids = OsRandom::try_new()?; + let mut tokens = ids.try_clone()?; + let mut fallback = tokens.clone(); + + println!("id : {}", ids.get_u64()?); + println!("token : {}", tokens.token(16)?); + println!("fallback: {}", fallback.hex(16)?); + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 6c716a2..59b9cc9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 { + 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