I have a function that returns a Result
type. Inside this function another function is being called which returns Result
type. And, if the returned value is an Err
variant, call a function/closure before bubbling up that error, else if it's Ok
variant call a function/closure and then simply return the contained value. This is what I've written so far
fn foo1() -> Result<i32, String> {
Err("error".to_string())
}
fn foo2() -> Result<String, bool> {
let res = foo1().map_err(|err| {
println!("foo1 failed {}", err);
false
})?;
// I know I can call some closure in here, but I want something like map_err which will call different closures depending on the Result variant
Ok(res.to_string())
}
fn main() {
foo2();
}
I would say you're probably at a point where your needs are sufficiently specialised that you should just drop down to a match
and implement the actual thing you need.
Combinators and higher order functions are there to signpost ways and provide semantic information to the reader, but if they don't apply straightforwardly or don't make things clearer it's perfectly fine and expected to not use them.
So we can desugar your snippet to this:
fn foo1() -> Result<i32, String> {
Err("error".to_string())
}
fn foo2() -> Result<String, bool> {
match foo1() {
Ok(res) => {
println!("foo1 succeeded {}", res);
Ok(res.to_string())
}
Err(err) => {
println!("foo1 failed {}", err);
Err(false)
}
}
}
fn main() {
let res = foo2();
match res {
Ok(_) => println!("succeeded"),
Err(_) => println!("failed")
}
}
and I don't think we're worse off for it. Quite the opposite in fact: the use of a raw match
actually signals to the reader (including future-you) that we're performing a non-obvious set of transformations.