Is there any operator similar to ? but returns unwrapped error instead of the result?

I am trying to use rocket.rs and in the handlers i need to wrap the data inside a responder which doesnt really play nice with ? operator since it returns a result.
What i would like to do is

let data = foo.map_err(|er|responder(er))//some op for unwrapreturning;

Without this in every endpoint i need to write

if data.is_err(){
    return responder(data.unwrap_err())
}

multiple times which gets really annoying over time since rocket was supposed to cut boilerplate down more then half of my code becomes glue code

PS. I am also opened to alternative http frameworks which handles things automatically like form parsing and returning json similar to rocket or actix

It seems the trait Responder already implemented for Result<impl Responder, impl Responder + Debug>

https://docs.rs/rocket/0.4.2/rocket/response/trait.Responder.html#impl-Responder<'r>-for-Result<R%2C%20E>

Oh that might just save my life let me see how it works:) currently i am dealing with some crazy nested type signatures :slight_smile:

For this code pattern, prefer a match / if let to classic method calls (since they allow to get both the variant case AND the inner value simultaneously).

if let Err(err) = data { // "if data.is_err { let err = data.unwrap_err();"
    return err;
}

Now, although @Hyeonu XY-solved your issue, regarding the OP itself:

The best solution here is to make your function be a helper wrapped function that returns Result<Ret, Ret>, and then do the unwrapping only once, from the wrapping function.

fn your_function (...args) -> Ret
{
    return helper(...args).unwrap_or_else(|err| err);
    // where
    fn helper (...args) -> Result<Ret, Ret>
    {
        let data = foo.map_err(...)?;
        let stuff = try_other_stuff.map_err(...)?;
    }
}

More generally, given a common error type Err for the ? to work without .map_err() calls, and a |err: Err| -> Ret { ... } common transformationm you can do:

fn your_function (...args) -> Ret
{
    return helper(...args).unwrap_or_else(|err: Err| -> Ret { ... });
    // where
    fn helper (...args) -> Result<Ret, Err>
    {
        let data = foo?;
        let stuff = try_other_stuff?;
    }
}

In other words, don't be afraid to factor out code into helper functions; it's one of the best ways to make ? just work.

You can also use a closure if you do not want to write the whole function's prototype twice:

fn your_function (...args) -> Ret
{
    (move || -> Result<Ret, Err> {
        let data = foo?;
        let stuff = try_other_stuff?;
    })().unwrap_or_else(|err| ...)
}

but the resulting code is quite ugly imho.


A final alternative, quite used in FFI since there are no Results allowed in the return type, is to define your own helper macro for your "unwrapping logic":

fn your_function (...args) -> Ret
{
    macro_rules! unwrap {($result:expr) => ({
        use ::core::result::Result::*;
        match $result {
            | Ok(it) => it,
            | Err(err) => return err,
        }
    })}
    let data = unwrap!(foo.map_err(...));
    let stuff = unwrap!(try_other_stuff.map_err(...));
}
  • Maybe some day in rust we will be able to have postfix macros, allowing the above code to be written as something along the lines of:

    fn your_function (...args) -> Ret
    {
        macro_rules! .unwrap {() => ({
            use ::core::result::Result::*;
            match $self {
                | Ok(it) => it,
                | Err(err) => return err,
            }
        })}
        let data = foo.map_err(...).unwrap!();
        let stuff = try_other_stuff.map_err(...).unwrap!();
    }
    
2 Likes

I think the correct approach is to use your custom error type, and implement Responder for that type. This way ? will convert to your error type, and formatting of the error will be handled by the error itself.