Skip to content
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

Error handling #7

Merged
merged 4 commits into from
Oct 21, 2017
Merged

Error handling #7

merged 4 commits into from
Oct 21, 2017

Conversation

pitdicker
Copy link

@pitdicker pitdicker commented Sep 30, 2017

This PR tries out some of the error handling we seem to be arriving at in the RFC tread.

For the error enum I have now:

pub enum Error {
    /// This RNG is permanently unavailable.
    NotAvailable,
    /// Needs (re)seeding. Depending on the RNG this can mean you have to wait
    /// and retry later, or you have to (re)seed it.
    EntropyLow,
    /// Failure, retry for a limited number of times.
    TemporaryFailure,
    /// Interrupted, usually by a signal from the OS.
    Interrupted,
    /// Any RNG error not part of this list.
    Other,
    #[doc(hidden)]
    __Nonexhaustive,
}

It occurred to me the variants EntropyExhausted and NetYetSeeded on the RFC thread are essentially the same, so I combined them into EntropyLow.

This commit partly undoes your work in 0a97209, I am sorry.
Maybe it is just me being to inexperienced, but the Result with two arguments looks clearer to me. I think it helps documentation-wise, as you can now click on the return type RngError in rustdoc and see the various causes. Because we have only four different methods that can return a Result, it does not really seem an ergonomic loss.

The second commit adds back fill_bytes. I gave try_fill a default implementation in terms of fill_bytes. This makes implementation of PRNGs (which usually never error) slightly simpler, as they don't have to use Result. Wrappers become slightly more complex, as they have to wrap another method.

In the third commit I tried out the new error handling with ReadRng.

Actually I was working on the OsRng implementations, and did not plan on writing this :-).
So I have not touched the code in os.rs that much yet.

@pitdicker
Copy link
Author

I am slowly getting better at rebasing 😄

pub struct Error;

#[cfg(feature="std")]
impl From<::std::io::Error> for Error {
Copy link
Author

@pitdicker pitdicker Sep 30, 2017

Choose a reason for hiding this comment

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

I did not add this From implementation yet.
I actually had one, that would map the IO errors you could get for OsRng to the appropriate RngError.
But I am not sure it would always be correct for other RNGs implementations. It are the more exotic ones that produce errors...

Not adding one forces RNG implementations to put a little more thought into their error handling. Which might be a plus... For ReadRng the missing implementation did not really make it more difficult, and I also expect it not to for OsRng.

Copy link
Owner

Choose a reason for hiding this comment

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

This sounds sensible to me.

@@ -76,8 +76,8 @@ impl Rng for MockAddRng<u64> {
impls::next_u128_via_u64(self)
}

fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> {
impls::try_fill_via_u32(self, dest)
Copy link
Author

Choose a reason for hiding this comment

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

via_u32 was a typo maybe?

}
/// Error type for random number generators.
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub enum RngError {
Copy link
Owner

Choose a reason for hiding this comment

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

I'd prefer the name Error personally — less repetition and matches usual naming conventions.

Copy link
Author

Choose a reason for hiding this comment

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

I thought I was following some sort of convention after reading the Error Handling Chapter in TRPL again. But Error seems to be common, and it shorter. I will change it.

Also just noticed: It should implement the ::std::error::Error trait.

}

/// Result type (convenience type-def)
pub type Result<T> = ::std::result::Result<T, Error>;
Copy link
Owner

Choose a reason for hiding this comment

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

Why remove this?

Copy link
Author

Choose a reason for hiding this comment

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

Have you seen my reason in the PR description?

In part the reason is that I was bitten by the redefined Result type ;-).
But I don't really see a reason to have it, as we only use it in the trait definitions in a handful of places.

In a library that uses the std::result::Result, would users also import rand::Result? Or would they want to use the std one and import our rand::Error?

It seems to me (but I am inexperienced) browsing documentation and copying examples is easier without the redefine...

NotAvailable,
/// Needs (re)seeding. Depending on the RNG this can mean you have to wait
/// and retry later, or you have to (re)seed it.
EntropyLow,
Copy link
Owner

Choose a reason for hiding this comment

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

Not so keen on the doc or name for this one, and not sure if the two different things should be combined anyway — what's the advantage?

Possible doc: For PRNGs, this implies they should be (re)seeded. For external RNGs (e.g. OsRng) this implies you should wait and try again later.

Copy link
Author

Choose a reason for hiding this comment

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

Ok, I will split them

pub type Result<T> = ::std::result::Result<T, Error>;
fn cause(&self) -> Option<&::std::error::Error> {
None
}
Copy link
Owner

Choose a reason for hiding this comment

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

I don't think there's any point implementing cause if it just returns None; this is the default impl.

Copy link
Author

Choose a reason for hiding this comment

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

Didn't notice that. No, than I can happily remove it

@dhardy
Copy link
Owner

dhardy commented Oct 16, 2017

Ok, removing the pub type Result<T> = .. bit and some of the small clean-ups I'm happy to merge now. I'm less sure about fill_bytes and Error... I guess that's something still to be decided.

@pitdicker
Copy link
Author

I'm less sure about fill_bytes and Error... I guess that's something still to be decided.

Yes, the discussion has died down a bit, but I agree there are still some things open. I also have some unposted comments on my pc about it, that need finishing :-).

After playing with this Error enum a bit more, I miss that it can not return a cause. What do you think about a struct with this enum and a cause when using std, and with only the enum field for no_std?
Another try I did was storing only the OS errno as a cause in this gist. It has the advantage that it does no allocations, but can only contain this one error type.

@pitdicker
Copy link
Author

Rebased and includes the changes discussed today in #9, #10 and #12.

I have not tested the new error struct much yet.

@dhardy
Copy link
Owner

dhardy commented Oct 20, 2017

Regarding my previous comment — any chance you can split this PR? I hate reviewing stuff this big. But also it might be better to merge the Isaac stuff first.

@pitdicker
Copy link
Author

any chance you can split this PR?

I was careful to split the PR in 3 commits. Each should be the smallest change that keeps building.

It is your call. Shall I remove the second two commits, and make PR's for them when this one lands?

Copy link
Owner

@dhardy dhardy left a comment

Choose a reason for hiding this comment

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

Couple of minor things. Looks like two of my comments were addressed by a later commit.

if is_getrandom_available() {
return Ok(OsRng { inner: OsGetrandomRng });
}

let reader = File::open("/dev/urandom")?;
let reader = File::open("/dev/urandom").unwrap();
Copy link
Owner

Choose a reason for hiding this comment

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

Handle this error?

pub fn new() -> Result<OsRng> {
let reader = File::open("rand:")?;
pub fn new() -> Result<OsRng, Error> {
let reader = File::open("rand:").unwrap();
Copy link
Owner

Choose a reason for hiding this comment

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

And this one

src/os.rs Outdated
for s in v.chunks_mut(fuchsia_zircon::ZX_CPRNG_DRAW_MAX_LEN) {
pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> {
Copy link
Owner

Choose a reason for hiding this comment

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

Huh? Did this jump a line?

Copy link
Author

Choose a reason for hiding this comment

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

That is weird, will fix.

src/read.rs Outdated
match r.read(buf) {
Ok(0) => return Err(Error::new(ErrorKind::Unavailable, None)),
Ok(n) => buf = &mut mem::replace(&mut buf, &mut [])[n..],
Err(_) => return Err(Error::new(ErrorKind::Other, None)),
Copy link
Owner

Choose a reason for hiding this comment

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

Err(e) => return Err(Error::new(ErrorKind::Other, Box::new(e)))

src/read.rs Outdated
0 => return Err(Error),
n => buf = &mut mem::replace(&mut buf, &mut [])[n..],
match r.read(buf) {
Ok(0) => return Err(Error::new(ErrorKind::Unavailable, None)),
Copy link
Owner

Choose a reason for hiding this comment

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

Nothing you can do with the given error type, but it does feel like the cause is missing here

Copy link
Author

Choose a reason for hiding this comment

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

It is fixed in the third commit, that gives ReadRng better error handling

fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error>;
fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
Ok(self.fill_bytes(dest))
}
Copy link
Owner

Choose a reason for hiding this comment

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

Default impl... I'd like not to have this

Copy link
Author

Choose a reason for hiding this comment

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

Maybe we can discuss it in #12? Personally I don't see a problem, but I may be missing something.

Copy link
Owner

Choose a reason for hiding this comment

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

Copy link
Author

Choose a reason for hiding this comment

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

I will remove it

@pitdicker
Copy link
Author

For OsRng there is a lot of room for improvement. I was working on this in a branch, but wanted this PR to get merged first. For OsRng it is going to mean lots of changes... (not looking forward to testing it)

Also moved the `impl_uint_from_fill` macro from `os.rs` to `randcore`. I had to
modify its error handling anyway, and it is shared with `OsRng`.
@pitdicker
Copy link
Author

Removed the default implementation of try_fill. Is it ok if I do the OsRng parts in another PR?

@dhardy
Copy link
Owner

dhardy commented Oct 21, 2017

Yes, I guess so. I'll merge this. Do a merge commit rather than a rebase on your other branch please.

@dhardy dhardy merged commit a4e9885 into dhardy:master Oct 21, 2017
@pitdicker pitdicker deleted the error_enum branch October 21, 2017 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants