Course contentsShow
← Rust
Lesson 828 of 1,9107. Error Handling PatternsFree lesson

Propagating Errors with the Question Mark

Refactor nested match statements into clean, readable code using the propagation operator.

In this lesson, you will learn how to replace cumbersome match statements with the concise ? operator to propagate errors upstream.

The Verbosity Problem

When handling operations that might fail, you have likely been using match to check if a Result is Ok or Err. If you find yourself nesting these matches—for example, reading a file, parsing its contents, and then processing a value—your code quickly becomes deeply indented and difficult to read. This is often called "the pyramid of doom."

Refactoring with the ? Operator

The ? operator is Rust’s elegant solution to this problem. When you place a ? after an expression that returns a Result, Rust performs a hidden check for you:

  1. If the result is Ok(value), it unwraps the value and continues execution.
  2. If the result is Err(e), it immediately returns that error from your current function.

Consider this nested approach:

fn read_and_parse(path: &str) -> Result<i32, MyError> {
    let content = match read_to_string(path) {
        Ok(c) => c,
        Err(e) => return Err(e.into()),
    };
    
    match content.trim().parse::<i32>() {
        Ok(num) => Ok(num),
        Err(e) => Err(e.into()),
    }
}

By using the ? operator, we can flatten that entire logic into just two lines:

fn read_and_parse(path: &str) -> Result<i32, MyError> {
    let content = read_to_string(path)?;
    let num = content.trim().parse::<i32>()?;
    Ok(num)
}

Why This Works

The ? operator doesn't just return the error; it automatically performs a conversion using the From trait. This means that if your function expects a specific error type, the ? operator will attempt to convert the underlying error into your function's error type, provided the conversion is defined. This keeps your business logic clean while maintaining full control over how errors flow through your program. You are no longer writing boilerplate; you are describing the "happy path" of your application.

Key Takeaway: The ? operator simplifies error handling by unwrapping Ok values or immediately returning Err values, allowing you to flatten nested code into a readable, linear flow.