Error Handling

Operator

The ? operator (the try operator) is a postfix unary operator that unwraps a successful value (Ok(T) or Some(T)) and evaluates to T, or returns early from the enclosing function with the failure (Err(E) or None).

How expr? Evaluates

When placed after a Result<T, E> expression inside a function returning Result<U, F>, expr? expands conceptually to a match expression that converts the error via From::from:

let val = fallible_call()?;
            │
            ├── Ok(v)  ──> binds `val = v` and continues execution
            └── Err(e) ──> returns `Err(From::from(e))` from the current function
// Using `?`:
fn parse_pair_question(a: &str, b: &str) -> Result<(i32, i32), std::num::ParseIntError> {
    let x = a.parse::<i32>()?;
    let y = b.parse::<i32>()?;
    Ok((x, y))
}

// Equivalent manual `match`:
fn parse_pair_match(a: &str, b: &str) -> Result<(i32, i32), std::num::ParseIntError> {
    let x = match a.parse::<i32>() {
        Ok(v) => v,
        Err(e) => return Err(From::from(e)),
    };
    let y = match b.parse::<i32>() {
        Ok(v) => v,
        Err(e) => return Err(From::from(e)),
    };
    Ok((x, y))
}

fn main() {
    println!("{:?}", parse_pair_question("10", "25"));
    println!("{:?}", parse_pair_match("10", "bad"));
}
Ok((10, 25))
Err(ParseIntError { kind: InvalidDigit })

Chaining Method Calls with ?

Because ? is a postfix operator (written after the expression rather than before it), multiple fallible operations can be chained left-to-right in a single expression:

use std::num::ParseIntError;

fn sum_first_two_csv(line: &str) -> Result<i32, ParseIntError> {
    let mut parts = line.split(',');
    let first = parts.next().unwrap_or("0").trim().parse::<i32>()?;
    let second = parts.next().unwrap_or("0").trim().parse::<i32>()?;
    Ok(first + second)
}

fn main() {
    println!("Valid:   {:?}", sum_first_two_csv(" 14, 28, 42 "));
    println!("Invalid: {:?}", sum_first_two_csv(" 14, oops, 42 "));
}
Valid:   Ok(42)
Invalid: Err(ParseIntError { kind: InvalidDigit })

Using ? on Option<T>

Inside a function that returns Option<U>, applying ? to an Option<T> unwraps Some(v) or returns None immediately on the first missing value:

fn middle_initial(full_name: &str) -> Option<char> {
    let mut words = full_name.split_whitespace();
    let _first = words.next()?;
    let middle = words.next()?;
    let _last = words.next()?;
    middle.chars().next()
}

fn main() {
    println!("Three words: {:?}", middle_initial("Ada Lovelace Byron"));
    println!("Two words:   {:?}", middle_initial("Alan Turing"));
}
Three words: Some('L')
Two words:   None

Return-Type Compatibility Rules

The ? operator can only be used inside a function (or closure) whose return type is compatible with the expression being tried:

Expression Before ? Required Enclosing Return Type
Result<T, E> Result<U, F> where F: From<E>
Option<T> Option<U>
ops::ControlFlow<B, C> ops::ControlFlow<B, D>

Using ? in a function that returns () or mixing Option<T> and Result<T, E> directly without converting (opt.ok_or(err)? or res.ok()?) produces error E0277:

// ❌ does not compile
fn log_number(s: &str) {
    let n: i32 = s.parse()?;
    println!("{n}");
}

fn main() {
    log_number("42");
}
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
 --> main.rs:2:27
  |
1 | fn log_number(s: &str) {
  | ---------------------- this function should return `Result` or `Option` to accept `?`
2 |     let n: i32 = s.parse()?;
  |                           ^ cannot use the `?` operator in a function that returns `()`