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");
}
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");
};
an alternative is Result<T, snafu::Whatever>
:
let Ok(x) = v else {
whatever!("error message");
};
Could you map_err()
, and just return the type?
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.
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(())
}
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.
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
.