-
Notifications
You must be signed in to change notification settings - Fork 432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New error handling + type #225
Changes from all commits
1136240
a68cb14
230b225
fed7943
c9bfa3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
// Copyright 2017-2018 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
//! Error types | ||
|
||
use core::fmt; | ||
|
||
#[cfg(feature="std")] | ||
use std::error::Error as stdError; | ||
|
||
/// Error kind which can be matched over. | ||
#[derive(PartialEq, Eq, Debug, Copy, Clone)] | ||
pub enum ErrorKind { | ||
/// Permanent failure: likely not recoverable without user action. | ||
Unavailable, | ||
/// Temporary failure: recommended to retry a few times, but may also be | ||
/// irrecoverable. | ||
Transient, | ||
/// Not ready yet: recommended to try again a little later. | ||
NotReady, | ||
/// Uncategorised error | ||
Other, | ||
#[doc(hidden)] | ||
__Nonexhaustive, | ||
} | ||
|
||
impl ErrorKind { | ||
/// True if this kind of error may resolve itself on retry. | ||
/// | ||
/// See also `should_wait()`. | ||
pub fn should_retry(self) -> bool { | ||
match self { | ||
ErrorKind::Transient | ErrorKind::NotReady => true, | ||
_ => false, | ||
} | ||
} | ||
|
||
/// True if we should retry but wait before retrying | ||
/// | ||
/// This implies `should_retry()` is true. | ||
pub fn should_wait(self) -> bool { | ||
self == ErrorKind::NotReady | ||
} | ||
|
||
/// A description of this error kind | ||
pub fn description(self) -> &'static str { | ||
match self { | ||
ErrorKind::Unavailable => "permanent failure or unavailable", | ||
ErrorKind::Transient => "transient failure", | ||
ErrorKind::NotReady => "not ready yet", | ||
ErrorKind::Other => "uncategorised", | ||
ErrorKind::__Nonexhaustive => unreachable!(), | ||
} | ||
} | ||
} | ||
|
||
/// Error type of random number generators | ||
/// | ||
/// This is a relatively simple error type, designed for compatibility with and | ||
/// without the Rust `std` library. It embeds a "kind" code, a message (static | ||
/// string only), and an optional chained cause (`std` only). | ||
#[derive(Debug)] | ||
pub struct Error { | ||
kind: ErrorKind, | ||
msg: &'static str, | ||
#[cfg(feature="std")] | ||
cause: Option<Box<stdError + Send + Sync>>, | ||
} | ||
|
||
impl Error { | ||
/// Create a new instance, with specified kind and a message. | ||
pub fn new(kind: ErrorKind, msg: &'static str) -> Self { | ||
#[cfg(feature="std")] { | ||
Error { kind: kind, msg: msg, cause: None } | ||
} | ||
#[cfg(not(feature="std"))] { | ||
Error { kind: kind, msg: msg } | ||
} | ||
} | ||
|
||
/// Create a new instance, with specified kind, message, and a | ||
/// chained cause. | ||
/// | ||
/// Note: `stdError` is an alias for `std::error::Error`. | ||
/// | ||
/// If not targetting `std` (i.e. `no_std`), this function is replaced by | ||
/// another with the same prototype, except that there are no bounds on the | ||
/// type `E` (because both `Box` and `stdError` are unavailable), and the | ||
/// `cause` is ignored. | ||
#[cfg(feature="std")] | ||
pub fn with_cause<E>(kind: ErrorKind, msg: &'static str, cause: E) -> Self | ||
where E: Into<Box<stdError + Send + Sync>> | ||
{ | ||
Error { kind: kind, msg: msg, cause: Some(cause.into()) } | ||
} | ||
|
||
/// Create a new instance, with specified kind, message, and a | ||
/// chained cause. | ||
/// | ||
/// In `no_std` mode the *cause* is ignored. | ||
#[cfg(not(feature="std"))] | ||
pub fn with_cause<E>(kind: ErrorKind, msg: &'static str, _cause: E) -> Self { | ||
Error { kind: kind, msg: msg } | ||
} | ||
|
||
/// Get the error kind | ||
pub fn kind(&self) -> ErrorKind { | ||
self.kind | ||
} | ||
|
||
/// Get the error message | ||
pub fn msg(&self) -> &'static str { | ||
self.msg | ||
} | ||
} | ||
|
||
impl fmt::Display for Error { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "RNG error [{}]: {}", self.kind.description(), self.msg()) | ||
} | ||
} | ||
|
||
#[cfg(feature="std")] | ||
impl stdError for Error { | ||
fn description(&self) -> &str { | ||
self.msg | ||
} | ||
|
||
fn cause(&self) -> Option<&stdError> { | ||
self.cause.as_ref().map(|e| e.as_ref() as &stdError) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -253,7 +253,6 @@ | |
use core::marker; | ||
use core::mem; | ||
#[cfg(feature="std")] use std::cell::RefCell; | ||
#[cfg(feature="std")] use std::io; | ||
#[cfg(feature="std")] use std::rc::Rc; | ||
|
||
// external rngs | ||
|
@@ -265,6 +264,9 @@ pub use isaac::{IsaacRng, Isaac64Rng}; | |
pub use chacha::ChaChaRng; | ||
pub use prng::XorShiftRng; | ||
|
||
// error types | ||
pub use error::{ErrorKind, Error}; | ||
|
||
// local use declarations | ||
#[cfg(target_pointer_width = "32")] | ||
use prng::IsaacRng as IsaacWordRng; | ||
|
@@ -294,6 +296,7 @@ pub mod isaac { | |
} | ||
|
||
// private modules | ||
mod error; | ||
mod rand_impls; | ||
mod prng; | ||
|
||
|
@@ -449,6 +452,22 @@ pub trait Rng { | |
impls::fill_bytes_via_u64(self, dest) | ||
} | ||
|
||
/// Fill `dest` entirely with random data. | ||
/// | ||
/// This is the only method which allows an RNG to report errors while | ||
/// generating random data; other methods either handle the error | ||
/// internally or panic. This method is | ||
/// the intended way to use external (true) RNGs, like `OsRng`. Its main | ||
/// use-cases are to generate keys and to seed (infallible) PRNGs. | ||
/// | ||
/// Other than error handling, this method is identical to [`fill_bytes`], and | ||
/// has a default implementation simply wrapping [`fill_bytes`]. | ||
/// | ||
/// [`fill_bytes`]: trait.Rng.html#method.fill_bytes | ||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||
Ok(self.fill_bytes(dest)) | ||
} | ||
|
||
/// Return a random value of a `Rand` type. | ||
/// | ||
/// # Example | ||
|
@@ -603,48 +622,68 @@ pub trait Rng { | |
} | ||
|
||
impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { | ||
#[inline] | ||
fn next_u32(&mut self) -> u32 { | ||
(**self).next_u32() | ||
} | ||
|
||
#[inline] | ||
fn next_u64(&mut self) -> u64 { | ||
(**self).next_u64() | ||
} | ||
|
||
#[inline] | ||
fn next_f32(&mut self) -> f32 { | ||
(**self).next_f32() | ||
} | ||
|
||
#[inline] | ||
fn next_f64(&mut self) -> f64 { | ||
(**self).next_f64() | ||
} | ||
|
||
#[inline] | ||
fn fill_bytes(&mut self, dest: &mut [u8]) { | ||
(**self).fill_bytes(dest) | ||
} | ||
|
||
#[inline] | ||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||
(**self).try_fill_bytes(dest) | ||
} | ||
} | ||
|
||
#[cfg(feature="std")] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not part of this PR, but should this also be available with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With that I mean There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah. Probably. Make a separate PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay. But I should probably wait until this PR has landed because they will conflict? |
||
impl<R: ?Sized> Rng for Box<R> where R: Rng { | ||
#[inline] | ||
fn next_u32(&mut self) -> u32 { | ||
(**self).next_u32() | ||
} | ||
|
||
#[inline] | ||
fn next_u64(&mut self) -> u64 { | ||
(**self).next_u64() | ||
} | ||
|
||
#[inline] | ||
fn next_f32(&mut self) -> f32 { | ||
(**self).next_f32() | ||
} | ||
|
||
#[inline] | ||
fn next_f64(&mut self) -> f64 { | ||
(**self).next_f64() | ||
} | ||
|
||
#[inline] | ||
fn fill_bytes(&mut self, dest: &mut [u8]) { | ||
(**self).fill_bytes(dest) | ||
} | ||
|
||
#[inline] | ||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||
(**self).try_fill_bytes(dest) | ||
} | ||
} | ||
|
||
/// Iterator which will generate a stream of random items. | ||
|
@@ -777,7 +816,7 @@ impl StdRng { | |
/// Reading the randomness from the OS may fail, and any error is | ||
/// propagated via the `io::Result` return value. | ||
#[cfg(feature="std")] | ||
pub fn new() -> io::Result<StdRng> { | ||
pub fn new() -> Result<StdRng, Error> { | ||
match OsRng::new() { | ||
Ok(mut r) => Ok(StdRng { rng: r.gen() }), | ||
Err(e1) => { | ||
|
@@ -807,6 +846,11 @@ impl Rng for StdRng { | |
fn fill_bytes(&mut self, dest: &mut [u8]) { | ||
self.rng.fill_bytes(dest) | ||
} | ||
|
||
#[inline] | ||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||
self.rng.try_fill_bytes(dest) | ||
} | ||
} | ||
|
||
impl<'a> SeedableRng<&'a [usize]> for StdRng { | ||
|
@@ -891,10 +935,12 @@ pub fn thread_rng() -> ThreadRng { | |
|
||
#[cfg(feature="std")] | ||
impl Rng for ThreadRng { | ||
#[inline] | ||
fn next_u32(&mut self) -> u32 { | ||
self.rng.borrow_mut().next_u32() | ||
} | ||
|
||
#[inline] | ||
fn next_u64(&mut self) -> u64 { | ||
self.rng.borrow_mut().next_u64() | ||
} | ||
|
@@ -903,6 +949,11 @@ impl Rng for ThreadRng { | |
fn fill_bytes(&mut self, bytes: &mut [u8]) { | ||
self.rng.borrow_mut().fill_bytes(bytes) | ||
} | ||
|
||
#[inline] | ||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||
self.rng.borrow_mut().try_fill_bytes(dest) | ||
} | ||
} | ||
|
||
/// Generates a random value using the thread-local random number generator. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as a test I removed the default implementation, and you did not miss to implement it for any wrappers etc. 👍.
I would like to see the default implementations be removed, but good to wait until all the changes for RNG implementations are merged.