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

Unwrapper trait to facilitate infallible methods #2191

Closed
burdges opened this issue Oct 27, 2017 · 1 comment
Closed

Unwrapper trait to facilitate infallible methods #2191

burdges opened this issue Oct 27, 2017 · 1 comment
Labels
T-libs-api Relevant to the library API team, which will review and decide on the RFC.

Comments

@burdges
Copy link

burdges commented Oct 27, 2017

There was a minor hiccup in the rand crate redesign in doing error handling: At present, Rng provides only infallible methods because you never need to worry about error in numerical use cases, but the cryptography changes this calculation, the operating system PRNG OsRng is not infallible, especially not in embedded or system startup use cases, and even deterministic CSPRNGs can encounter errors.

I suspect the general solution might involve standard tooling for parameterizing error handling at the type level.

pub trait Unwraper<E> {
    fn unwraper<T>(&self, r: Result<T,E>) -> T
}
pub struct Unrwap;
impl<E> Unwraper<E> for Unrwap {
    fn unwraper<T>(&self, r: Result<T,E>) -> T { r.unwrap() }
}
pub struct Expect<const S: &'static str>;
impl<E> Unwraper<E> for Expect<S> {
    fn unwraper<T>(&self, r: Result<T,E>) -> T { r.expect(S) }
}
pub struct UnwrapOrElse<E,F>(f: &F) where F: Fn(E) -> T;
impl<E,F> Unwraper<E> for UnwrapOrElse<E,const f: F> where F: Fn(E) -> T {
    fn unwraper<T>(&self, r: Result<T,E>) -> T { r.unwrap_or_else(self.0) }
}

In principle, there could be an UnwrapperMut<E> trait with a mutable receiver &mut self, but maybe interior mutability suffices, and/or an UnwrapperType<E> trait with only a type-level receiver, i.e. no self parameter, but maybe optimization eliminates the need.

We have a typical situation in which a trait exists in both fallible and infallible forms:

pub trait TryRng {
    type Error;
    fn try_next_u32(&mut self) -> Result<u32,Error>;
    ...
}
pub struct OsRng;
impl TryRng for OsRng ...
...
pub trait Rng {
    fn next_u32(&mut self) -> u32;
    ...
}

We can now supply a generic impl for the infallible form paired with an Unwraper from the fallible form:

impl<R,U,E> Rng for (R,U) where R: TryRng<Error=E>, U: Unwapper<E> {
    fn next_u32(&mut self) -> u32 { self.1.unwrapper(self.0.try_next_u32()) }
    ...
}

In this way, we obtain the infallible form Rng easily from a tuple like (rand::OsRngnew().expect("Not even!!"),Expect<const "oopsy daisy!!">).

As an aside, there are serious issues with using unwinding for flow control, so it should not be encouraged, but one could seemingly build Unwrapper<E> instances that do so:

pub struct Catcher<'a,E> {
    err: &Cell<Option<E>>
}
impl<'a,E> Unwraper<E> for Catcher<'a> {
    fn unwraper<T>(&self, r: Result<T,E>) -> T {
        r.unwrap_or_else(|| {
            assert!(self.err.replace(e) == None); 
            ::std::panic::resume_unwind(box ())  // panic!() needed?
        })
    }
}
pub fn do_catcher<'a,E,F>(f: F) -> Result<E> where F: FnOnce(c: Catcher<'a,E>) -> E + UnwindSafe {
    let err = Cell::new(None);
    ::std::panic::catch_unwind(|| {
        f(Catcher { &err })
    }).map_err(|e| Err(
        err.get().unwrap_or_else(|| ::std::panic::resume_unwind(e))
    ))
}
@burdges burdges changed the title Unwrapper traits: Towards an exceptions monads Unwrapper traits: Towards exceptions monads Oct 27, 2017
@burdges burdges changed the title Unwrapper traits: Towards exceptions monads Unwrapper trait to facilitate infallible methods Oct 27, 2017
@burdges
Copy link
Author

burdges commented Oct 31, 2017

I'm going to close this since it could just develop outside std if it makes any sense.

@burdges burdges closed this as completed Oct 31, 2017
@Centril Centril added the T-libs-api Relevant to the library API team, which will review and decide on the RFC. label Feb 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-libs-api Relevant to the library API team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

2 participants