Skip to content

Commit 5c7bbea

Browse files
authored
Merge pull request #800 from dhardy/core_error
Revise rand_core::Error (alt #771)
2 parents 5b31f2d + 2d55947 commit 5c7bbea

File tree

17 files changed

+244
-303
lines changed

17 files changed

+244
-303
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ before_install:
268268

269269
script:
270270
- cargo test --tests --no-default-features
271+
- cargo test --no-default-features --features getrandom
271272
# TODO: add simd_support feature:
272273
- cargo test --features=serde1,log
273274
- cargo test --examples

Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ i128_support = [] # enables i128 and u128 support
2828
simd_support = ["packed_simd"] # enables SIMD support
2929
serde1 = ["rand_core/serde1", "rand_isaac/serde1", "rand_xorshift/serde1"] # enables serialization for PRNGs
3030
# re-export optional WASM dependencies to avoid breakage:
31-
wasm-bindgen = ["getrandom/wasm-bindgen"]
32-
stdweb = ["getrandom/stdweb"]
31+
wasm-bindgen = ["getrandom_package/wasm-bindgen"]
32+
stdweb = ["getrandom_package/stdweb"]
33+
getrandom = ["getrandom_package", "rand_core/getrandom"]
3334

3435
[workspace]
3536
members = [
@@ -50,7 +51,8 @@ members = [
5051
rand_core = { path = "rand_core", version = "0.4" }
5152
rand_pcg = { path = "rand_pcg", version = "0.1" }
5253
rand_hc = { path = "rand_hc", version = "0.1" }
53-
getrandom = { version = "0.1.1", optional = true }
54+
# Do not depend on 'getrandom_package' directly; use the 'getrandom' feature!
55+
getrandom_package = { version = "0.1.1", package = "getrandom", optional = true }
5456
log = { version = "0.4", optional = true }
5557

5658
[dependencies.packed_simd]

rand_core/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ travis-ci = { repository = "rust-random/rand" }
1818
appveyor = { repository = "rust-random/rand" }
1919

2020
[features]
21-
std = ["alloc"] # use std library; should be default but for above bug
21+
std = ["alloc", "getrandom", "getrandom/std"] # use std library; should be default but for above bug
2222
alloc = [] # enables Vec and Box support without std
2323
serde1 = ["serde", "serde_derive"] # enables serde for BlockRng wrapper
2424

2525
[dependencies]
2626
serde = { version = "1", optional = true }
2727
serde_derive = { version = "^1.0.38", optional = true }
28+
getrandom = { version = "0.1", optional = true }

rand_core/src/error.rs

Lines changed: 85 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -9,169 +9,126 @@
99
//! Error types
1010
1111
use core::fmt;
12-
13-
#[cfg(feature="std")]
14-
use std::error::Error as stdError;
15-
#[cfg(feature="std")]
16-
use std::io;
17-
18-
/// Error kind which can be matched over.
19-
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
20-
pub enum ErrorKind {
21-
/// Feature is not available; not recoverable.
22-
///
23-
/// This is the most permanent failure type and implies the error cannot be
24-
/// resolved simply by retrying (e.g. the feature may not exist in this
25-
/// build of the application or on the current platform).
26-
Unavailable,
27-
/// General failure; there may be a chance of recovery on retry.
28-
///
29-
/// This is the catch-all kind for errors from known and unknown sources
30-
/// which do not have a more specific kind / handling method.
31-
///
32-
/// It is suggested to retry a couple of times or retry later when
33-
/// handling; some error sources may be able to resolve themselves,
34-
/// although this is not likely.
35-
Unexpected,
36-
/// A transient failure which likely can be resolved or worked around.
37-
///
38-
/// This error kind exists for a few specific cases where it is known that
39-
/// the error likely can be resolved internally, but is reported anyway.
40-
Transient,
41-
/// Not ready yet: recommended to try again a little later.
42-
///
43-
/// This error kind implies the generator needs more time or needs some
44-
/// other part of the application to do something else first before it is
45-
/// ready for use; for example this may be used by external generators
46-
/// which require time for initialization.
47-
NotReady,
48-
#[doc(hidden)]
49-
__Nonexhaustive,
50-
}
51-
52-
impl ErrorKind {
53-
/// True if this kind of error may resolve itself on retry.
54-
///
55-
/// See also `should_wait()`.
56-
pub fn should_retry(self) -> bool {
57-
self != ErrorKind::Unavailable
58-
}
59-
60-
/// True if we should retry but wait before retrying
61-
///
62-
/// This implies `should_retry()` is true.
63-
pub fn should_wait(self) -> bool {
64-
self == ErrorKind::NotReady
65-
}
66-
67-
/// A description of this error kind
68-
pub fn description(self) -> &'static str {
69-
match self {
70-
ErrorKind::Unavailable => "permanently unavailable",
71-
ErrorKind::Unexpected => "unexpected failure",
72-
ErrorKind::Transient => "transient failure",
73-
ErrorKind::NotReady => "not ready yet",
74-
ErrorKind::__Nonexhaustive => unreachable!(),
75-
}
76-
}
77-
}
12+
use core::num::NonZeroU32;
7813

7914

8015
/// Error type of random number generators
81-
///
82-
/// This is a relatively simple error type, designed for compatibility with and
83-
/// without the Rust `std` library. It embeds a "kind" code, a message (static
84-
/// string only), and an optional chained cause (`std` only). The `kind` and
85-
/// `msg` fields can be accessed directly; cause can be accessed via
86-
/// `std::error::Error::cause` or `Error::take_cause`. Construction can only be
87-
/// done via `Error::new` or `Error::with_cause`.
16+
///
17+
/// In order to be compatible with `std` and `no_std`, this type has two
18+
/// possible implementations: with `std` a boxed `Error` trait object is stored,
19+
/// while with `no_std` we merely store an error code.
8820
#[derive(Debug)]
8921
pub struct Error {
90-
/// The error kind
91-
pub kind: ErrorKind,
92-
/// The error message
93-
pub msg: &'static str,
9422
#[cfg(feature="std")]
95-
cause: Option<Box<stdError + Send + Sync>>,
23+
inner: Box<dyn std::error::Error + Send + Sync + 'static>,
24+
#[cfg(not(feature="std"))]
25+
code: NonZeroU32,
9626
}
9727

9828
impl Error {
99-
/// Create a new instance, with specified kind and a message.
100-
pub fn new(kind: ErrorKind, msg: &'static str) -> Self {
101-
#[cfg(feature="std")] {
102-
Error { kind, msg, cause: None }
103-
}
104-
#[cfg(not(feature="std"))] {
105-
Error { kind, msg }
106-
}
107-
}
108-
109-
/// Create a new instance, with specified kind, message, and a
110-
/// chained cause.
29+
/// Construct from any type supporting `std::error::Error`
11130
///
112-
/// Note: `stdError` is an alias for `std::error::Error`.
31+
/// Available only when configured with `std`.
11332
///
114-
/// If not targetting `std` (i.e. `no_std`), this function is replaced by
115-
/// another with the same prototype, except that there are no bounds on the
116-
/// type `E` (because both `Box` and `stdError` are unavailable), and the
117-
/// `cause` is ignored.
33+
/// See also `From<NonZeroU32>`, which is available with and without `std`.
11834
#[cfg(feature="std")]
119-
pub fn with_cause<E>(kind: ErrorKind, msg: &'static str, cause: E) -> Self
120-
where E: Into<Box<stdError + Send + Sync>>
35+
pub fn new<E>(err: E) -> Self
36+
where E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>
12137
{
122-
Error { kind, msg, cause: Some(cause.into()) }
38+
Error { inner: err.into() }
12339
}
12440

125-
/// Create a new instance, with specified kind, message, and a
126-
/// chained cause.
41+
/// Reference the inner error (`std` only)
12742
///
128-
/// In `no_std` mode the *cause* is ignored.
129-
#[cfg(not(feature="std"))]
130-
pub fn with_cause<E>(kind: ErrorKind, msg: &'static str, _cause: E) -> Self {
131-
Error { kind, msg }
43+
/// When configured with `std`, this is a trivial operation and never
44+
/// panics. Without `std`, this method is simply unavailable.
45+
#[cfg(feature="std")]
46+
pub fn inner(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
47+
&*self.inner
13248
}
13349

134-
/// Take the cause, if any. This allows the embedded cause to be extracted.
135-
/// This uses `Option::take`, leaving `self` with no cause.
50+
/// Unwrap the inner error (`std` only)
51+
///
52+
/// When configured with `std`, this is a trivial operation and never
53+
/// panics. Without `std`, this method is simply unavailable.
13654
#[cfg(feature="std")]
137-
pub fn take_cause(&mut self) -> Option<Box<stdError + Send + Sync>> {
138-
self.cause.take()
55+
pub fn take_inner(self) -> Box<dyn std::error::Error + Send + Sync + 'static> {
56+
self.inner
57+
}
58+
59+
/// Retrieve the error code, if any.
60+
///
61+
/// If this `Error` was constructed via `From<NonZeroU32>`, then this method
62+
/// will return this `NonZeroU32` code (for `no_std` this is always the
63+
/// case). Otherwise, this method will return `None`.
64+
pub fn code(&self) -> Option<NonZeroU32> {
65+
#[cfg(feature="std")] {
66+
self.inner.downcast_ref::<ErrorCode>().map(|c| c.0)
67+
}
68+
#[cfg(not(feature="std"))] {
69+
Some(self.code)
70+
}
13971
}
14072
}
14173

14274
impl fmt::Display for Error {
14375
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
14476
#[cfg(feature="std")] {
145-
if let Some(ref cause) = self.cause {
146-
return write!(f, "{} ({}); cause: {}",
147-
self.msg, self.kind.description(), cause);
148-
}
77+
write!(f, "{}", self.inner)
78+
}
79+
#[cfg(not(feature="std"))] {
80+
write!(f, "error code {}", self.code)
14981
}
150-
write!(f, "{} ({})", self.msg, self.kind.description())
15182
}
15283
}
15384

154-
#[cfg(feature="std")]
155-
impl stdError for Error {
156-
fn description(&self) -> &str {
157-
self.msg
85+
impl From<NonZeroU32> for Error {
86+
fn from(code: NonZeroU32) -> Self {
87+
#[cfg(feature="std")] {
88+
Error { inner: Box::new(ErrorCode(code)) }
89+
}
90+
#[cfg(not(feature="std"))] {
91+
Error { code }
92+
}
15893
}
94+
}
15995

160-
fn cause(&self) -> Option<&stdError> {
161-
self.cause.as_ref().map(|e| e.as_ref() as &stdError)
96+
#[cfg(feature="getrandom")]
97+
impl From<getrandom::Error> for Error {
98+
fn from(error: getrandom::Error) -> Self {
99+
#[cfg(feature="std")] {
100+
Error { inner: Box::new(error) }
101+
}
102+
#[cfg(not(feature="std"))] {
103+
Error { code: error.code() }
104+
}
105+
}
106+
}
107+
108+
#[cfg(feature="std")]
109+
impl std::error::Error for Error {
110+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
111+
self.inner.source()
162112
}
163113
}
164114

165115
#[cfg(feature="std")]
166-
impl From<Error> for io::Error {
116+
impl From<Error> for std::io::Error {
167117
fn from(error: Error) -> Self {
168-
use std::io::ErrorKind::*;
169-
match error.kind {
170-
ErrorKind::Unavailable => io::Error::new(NotFound, error),
171-
ErrorKind::Unexpected |
172-
ErrorKind::Transient => io::Error::new(Other, error),
173-
ErrorKind::NotReady => io::Error::new(WouldBlock, error),
174-
ErrorKind::__Nonexhaustive => unreachable!(),
175-
}
118+
std::io::Error::new(std::io::ErrorKind::Other, error)
176119
}
177120
}
121+
122+
#[cfg(feature="std")]
123+
#[derive(Debug, Copy, Clone)]
124+
struct ErrorCode(NonZeroU32);
125+
126+
#[cfg(feature="std")]
127+
impl fmt::Display for ErrorCode {
128+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129+
write!(f, "error code {}", self.0)
130+
}
131+
}
132+
133+
#[cfg(feature="std")]
134+
impl std::error::Error for ErrorCode {}

0 commit comments

Comments
 (0)