Lets try to perform a calculation on multiple numbers parsed from strings:

let numbers_1: Result<u32, ParseIntError> = add_numbers("10", "20", "30"); // Ok(60)
let numbers_2 = add_numbers("ten", "20", "30");    // Err(ParseIntError { kind: InvalidDigit })
let numbers_3 = add_numbers("10", "twenty", "30"); // Err(ParseIntError { kind: InvalidDigit })
let numbers_4 = add_numbers("10", "20", "thirty"); // Err(ParseIntError { kind: InvalidDigit })

Here’s the definition of add_numbers:

fn add_numbers(one: &str, two: &str, three: &str) -> Result<u32, ParseIntError> {
  parse_number(one) // try and get the first number. Returns Result<u32, ParseIntError>
    .and_then(|n1| { // if that succeeds,
      parse_number(two) // try and get the second number. Returns Result<u32, ParseIntError>
        .and_then(|n2| { // if that succeeds
          parse_number(three) // try and get the third number. Returns Result<u32, ParseIntError>
            .map(|n3| n1 + n2 + n3) // if that succeeds, add up all the previous numbers. Returns Result<u32, ParseIntError>
        })
    })
}

This is similar to how we previously parsed two numbers. This is quickly becoming hard to reason about. Parsing more numbers like this would be almost unmaintainable. Luckily Rust gives us a simpler way to do this.

The question mark operator

Rust has the question mark operator (?) which allows you to simply return an error or extract a success value. You can think of it as an unwrap on Ok with an immediate return on Err, instead of panic-ing.

Here’s the definition of and_numbers_2 which uses the ? operator:

fn add_numbers_2(one: &str, two: &str, three: &str) -> Result<u32, ParseIntError> {
  let n1: u32 = parse_number(one)?;   // Get the number or return an Err
  let n2: u32 = parse_number(two)?;   // Get the number or return an Err
  let n3: u32 = parse_number(three)?; // Get the number or return an Err

  // If we got here, all the numbers are valid
  Ok(n1 + n2 + n3) // Add all the numbers and return an Ok
}

It’s important to note that if any of the parse_number function calls return an Err, the add_numbers_2 function would return that Err as the final result instead of proceeding to the next line.

We have to still wrap the final result in an Ok constructor as add_numbers_2 returns a Result<u32, ParseIntError>.

We can see that the add_numbers_2 function is easier to reason about than chaining together and_then and map calls as in the add_numbers function. The ? operator is supported for Result and Option types at the moment.

Keep aligning those error values

Something else to keep in mind is that we still need to align on the Err value as we did when using and_then:

fn add_numbers_3(one: &str, two: &str, three: &str) -> Result<u32, ParseIntError> {
  let n1: u32 = parse_number(one)?;                                      // Result<u32, ParseIntError>
  let n2: u32 = parse_number(two).map_err(|e| MyError(e.to_string()))?;  // Result<u32, MyError>
  let n3: u32 = parse_number(three)?;                                    // Result<u32, ParseIntError>

  // If we got here, all the numbers are valid
  Ok(n1 + n2 + n3) // Add all the numbers and return an Ok
}

The above leads to an error:

error[E0277]: `?` couldn't convert the error to `ParseIntError`
   --> src/main.rs:414:74
    |
412 | ...numbers_3(one: &str, two: &str, three: &str) -> Result<u32, ParseIntError> {
    |                                                    -------------------------- expected `ParseIntError` because of this
413 | ...1: u32 = parse_number(one)?; // Get the number or return an Err
414 | ...2: u32 = parse_number(two).map_err(|_| MyError("Blah".to_owned()))?; // Get the number...
    |             ----------------- ---------------------------------------^ the trait `From<MyError>` is not implemented for `ParseIntError`, which is required by `Result<u32, ParseIntError>: FromResidual<Result<Infallible, MyError>>`
    |             |                 |
    |             |                 this can't be annotated with `?` because it has type `Result<_, MyError>`
    |             this has type `Result<_, ParseIntError>`
    |
    = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
    = help: the following other types implement trait `FromResidual<R>`:
              <Result<T, F> as FromResidual<Yeet<E>>>
              <Result<T, F> as FromResidual<Result<Infallible, E>>>
    = note: required for `Result<u32, ParseIntError>` to implement `FromResidual<Result<Infallible, MyError>>`

The important bits are:

error[E0277]: ? couldn’t convert the error to ParseIntError

Result<u32, ParseIntError> ————————– expected ParseIntError because of this

the trait From<MyError> is not implemented for ParseIntError, which is required by Result<u32, ParseIntError>: FromResidual<Result<Infallible, MyError>>

the question mark operation (?) implicitly performs a conversion on the error value using the From trait

The error states that we need an Err value of type ParseIntError and we have an Err value of type MyError. If we have a From instance to convert from MyError to ParseIntError it would be called and the conversion automatically performed for us.

We can’t directly create a ParseIntError as the constructor is private. We can however create one from parsing a String that doesn’t represent a number. Using that information we can create a terrible From implementation to convert from MyError to ParseIntError:

impl From<MyError> for ParseIntError {
  fn from(source: MyError) -> Self {
      parse_number(&source.0).unwrap_err() // Forcing values again
  }
}

With the above conversion in place add_numbers_3 compiles with out any errors, indicating that MyError was implicitly converted to ParseIntError and aligning our Err values almost for “free”. The question mark operator makes working with Result so much easier.