2

I've observed several times a situation where I might want to act based on the ultimate result of a bunch of operations, where I don't care which operation threw the error, just that one of them did, or none of them did. I can use the ? operator for this, but it then propagates the error to the function call. I hoped that something like this would work:

fn do_something() {
 let i = {
  let w = function1()?;
  let x = function2(z)?.function3()?;
  x.y()?
 };
 if let Ok(item) = i {
  // ..
 } else {
  // call to some other function or something..
 }
 // some more code here..
}

However, this doesn't work. Is there some alternate way to achieve this pattern? I know I can wrap the scope in a closure, but this feels ugly and unidiomatic especially if such behaviour is required often. This might be indicative that I need to break the function into more smaller functions, however that proves unwieldy in situations like

 let i = {
  function1()?.function2()?.function3()
 }

where making an entirely new function doesn't quite make sense. Maybe this isn't even the idiomatic way to do things. If so, how should I approach this issue?

zombiesauce
  • 1,009
  • 1
  • 7
  • 22
  • 1
    You are looking for the [`try_blocks`](https://doc.rust-lang.org/nightly/unstable-book/language-features/try-blocks.html) feature which is currently unstable. Until then, you can chain multiple results with `Result::and_then`, but you'll have to convert the error case manually. – user2722968 Jul 24 '21 at 11:32

1 Answers1

2

Your example is not really reproducible, because it cannot be compiled. So I'll give an example of my own.

You can use functional approach to compose your method calls. Something like this:

fn main() {
    let result = one()
        .and_then(|_ok| two())// will  be called if one() returned Ok, otherwise `result will contain the returned Err
        .and_then(|_ok| three()); // will  be called if two() returned Ok, otherwise `result will contain the returned Err
    println!("Result: {:#?}", result);
}

fn one() -> Result<(), String> {
    println!("one");

    Ok(())
}

fn two() -> Result<(), String> {
    println!("two");

    Err("two".to_owned())
}

fn three() -> Result<(), String> {
    println!("tree"); // won;t be called because `two()` returned an err

    Ok(())
}

There are also other useful combinators such as or_else(), map_or(), map_err(), etc.

Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82