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>

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.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.