Alternative to expect() for returning a String error

So .expect("My Error") lets you panic with a message. I want to instead return an error with that message instead. Essentially a short hand for:

match v {
    Ok(x) => ...
    Err(e) => return Err("My Error");
}

you can use anyhow::bail!() for anyhow::anyhow!() if you use Result<T, anyhow::Error>

let Ok(x) = v else {
    bail!("erro rmessage");
};
1 Like

an alternative is Result<T, snafu::Whatever>:

let Ok(x) = v else {
    whatever!("error message");
};

Could you map_err(), and just return the type?

3 Likes

This is perfect because I was looking for a one liner. Error handling seems to be very verbose in Rust, and I think due to this most snippets just tend to use unwrap(). Still wish there was an easier way to do this like .expect(), but this is what I ended up doing:

let y = v.map_err(|_| format!("My Error: {}", x))?;

Given your reply just now, most of my initial reply is probably redundant.

But here it is anyway

The general shorthand for

match v {
    Ok(x) => ...
    Err(e) => return Err("My Error");
}

is the ? (Try) operator. The trait is complicated, but for Result, an usage would look like:

fn some_function<O, E>(v: Result<O, E>) -> Result<(), MyError>
where
    MyError: From<E>
{
    let x: O = v?;
    // ...
    Ok(())
}

For result, you can think of the desugaring as:[1]

    let x = match v {
        Ok(x) => x,
        Err(e) => return Err(e.into()),
    };

In the simplest case, the error type in your function signature is the same as the type in all of your errors.

fn some_function(v: Result<String, &'static str>) -> Result<(), &'static str> {
    let x: O = v?;
    // ...
    Ok(())
}

But you can also use utilities like map_err if that's not the case and there's no applicable Into implementation.

fn some_function<E>(v: Result<String, E>) -> Result<(), &'static str> {
    let x: O = v.map_err(|_| "my error message")?;
    // ...
    Ok(())
}

You can read more in the book here.


Snippets such as learning material and documentation examples? Yes, you're probably right, though rustdoc has better ? context support these days. More serious[2] code uses structured errors for the library side, which -- granted -- involves a lot of boilerplate (especially to look concise at the use site).[3] Or a generic error framework such as anyhow for a binary application where the errors are user-directed.


  1. Again the Try trait is more complicated, but from a practical POV it behaves like this ↩︎

  2. non-throwaway ↩︎

  3. Rust in general has a lot of boilerplate ↩︎

2 Likes

The alternative to this, that would be more concise, at the cost of an extra library, is using anyhow, and returning an anyhow error, with .context() on the error.

The upside of that form vs a method like expect is that the code to build the returned error is not executed when v is Ok. That may seem trivial, but you'll appreciate that advantage if building that message (or whatever you need to do) has an impact on your data, is time-consuming, or must access something that doesn't exist if v is Ok.

The same idea is found in methods like Option::or_else, Option::uwrap_or_else, Option::map_or_else, Option::and_then, Entry::or_insert_with, Entry::and_modify, etc. An easy mistake is to use Option::[...]_or instead of the form Option::[...]_or_else when it can potentially crash in the alternative, simply because its argument is evaluated no matter if it's Some or None.