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 Result
s 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!();
}