error-handlingrustcombinators

Conditionally Acting on Specific Error Using and_then in Rust


How do I know what error happened when piping Results via and_then in Rust? For example using nested matches I could do this:

use std::num::ParseIntError;

fn get_num(n: u8) -> Result<u8, ParseIntError> {
   // Force a ParseIntError
   "foo ".parse()
}
fn add_one(n: u8) -> Result<u8, ParseIntError> {
    Ok(n + 1)
}
fn main() {
    match get_num(1) {
        Ok(n) => {
            match add_one(n) {
                Ok(n) => println!("adding one gives us: {}", n),
                Err(why) => println!("Error adding: {}", why)
            }
        },
        Err(why) => println!("Error getting number: {}", why),
    }
}

Or I can use and_then to pipe the result of the first function to the second and avoid a nested match:

match get_num(1).and_then(add_one) {
  Ok(n) => println!("Adding one gives us: {}", n),
  Err(why) => println!("Some error: {}.", why)
}

How do I conditionally and idiomatically determine the error in the second form, using and_then? Do I have to type check the error? Sure I can display the error like I did above, but let's say I want to preface it with something related to the kind of error it is?


Solution

  • The normal way would be to use map_err to convert the initial errors to something richer you control e.g.

    enum MyError {
        Parsing,
        Adding
    }
    
    get_num(1).map_err(|_| MyError::Parsing)
        .and_then(|v| add_one(v).map_err(|_| MyError::Adding)
    

    sadly as you can see this requires using a lambda for the and_then callback: and_then must return the same error type as the input, so here it has to yield a Result<_, MyError>, wjhich is not the return type of add_one.

    And while in other situations we could recover somewhat with ?, here the same original error type is split into two different values.