← Back to Rust series
πŸ¦€
Error handling
Error handling Β· Prerequisite: lesson 15

16. The ? Operator and Error Propagation

The `?` operator after a Result or Option expression means "return on failure immediately," turning nested match into a single line. Combined with the `From` trait it auto-converts error types as it propagates up the call stack.

Rust?error propagationFromResult
Duration
⏱ ~1.5 hours
Level
πŸ“Š Intermediate
Prerequisite
🎯 Lesson 15
OUTCOME
The `?` operator after a Result or Option expression means "return on failure immediately," turning nested match into a single line. Combined with the `From` trait it auto-converts error types as it propagates up the call stack.

What you'll learn

  • 1Propagate Result/Option failures with `?`
  • 2Make main return Result so `?` works there
  • 3Wire automatic error conversion via the From trait
  • 4Know when `?` works on Option too
  • 5Use Box<dyn Error> for quick error aggregation

Overview

Match-on-every-step turns into a pyramid of indentation. The `?` character flattens that β€” "if this step fails, just return upward as is."

Core Concepts

1) ? on a Result

`expr?` is equivalent to:

rust
// expr?
// is shorthand for:
match expr {
    Ok(v)  => v,
    Err(e) => return Err(e.into()), // From-converted
}

2) ? on an Option

If the enclosing function returns Option, `?` returns None immediately on None.

3) Auto-conversion via From

If your error type `MyError` implements `From<io::Error>`, then `?` on an io operation auto-converts to `MyError`. This is how you unify several error types into one.

4) main can return Result too

rust
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
    let n = "42".parse::<i32>()?;
    println!("{}", n);
    Ok(())
}

Hands-on Examples

Open file and parse first line β€” without and with `?`:

rust
use std::fs::File;
use std::io::{self, Read};

// without ? β€” pyramid of indentation
fn read_first_number_v1(path: &str) -> Result<i32, String> {
    let mut f = match File::open(path) {
        Ok(f) => f,
        Err(e) => return Err(format!("open: {}", e)),
    };
    let mut s = String::new();
    if let Err(e) = f.read_to_string(&mut s) {
        return Err(format!("read: {}", e));
    }
    let first = s.lines().next().ok_or("empty")?;
    first.trim().parse::<i32>().map_err(|e| format!("parse: {}", e))
}
rust
// with ? and Box<dyn Error> β€” line by line
use std::error::Error;

fn read_first_number_v2(path: &str) -> Result<i32, Box<dyn Error>> {
    let mut s = String::new();
    File::open(path)?.read_to_string(&mut s)?;
    let first = s.lines().next().ok_or("empty")?;
    let n = first.trim().parse::<i32>()?;
    Ok(n)
}

With Option:

rust
fn first_char_uppercased(s: &str) -> Option<char> {
    let c = s.chars().next()?;          // None β†’ return None
    c.to_uppercase().next()
}

fn main() {
    println!("{:?}", first_char_uppercased("hello")); // Some('H')
    println!("{:?}", first_char_uppercased(""));      // None
}

Common Mistakes

Q. Does `?` only work inside a function?

A. Yes, and that function must return Result or Option. main can if you give it `Result<(), Box<dyn Error>>`. Outside of fallible contexts `?` is a compile error.

Q. The error types differ β€” does `?` still work?

A. Yes, as long as `From` is implemented between them. `Box<dyn Error>` is the catch-all type that accepts almost any error (handy for binaries; libraries should prefer concrete error types like `thiserror`).

Q. Can I use `?` in the middle of an expression?

A. Yes: `let n = parse()? + 1;`. Just needs to be in a place where the operand has type Result or Option.

Recap

  • `?` = single-character "return on failure"
  • Function must return Result or Option to use it
  • From-impl provides automatic error conversion
  • main returning Result lets you use `?` at the top level

Try It Yourself

  1. Two-step parse (string β†’ int β†’ positive check) using `?`
  2. Write `fn first_two_uppercased(s: &str) -> Option<(char, char)>` with `?`
  3. Refactor main to `-> Result<(), Box<dyn Error>>` and propagate file IO + parsing errors with `?`
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub β†—