Error Handling (the ? operator)

Rust is a systems programming language focused on speed, memory safety, and fearless concurrency — and it handles recoverable errors with Result rather than exceptions, made ergonomic by the ? operator.

Learn Error Handling (the ? operator) in our free Rust course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Rust course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll see how the ? operator replaces verbose match boilerplate to propagate errors cleanly up the call stack.

What You'll Learn in This Lesson

1️⃣ The Verbose Way: Propagating with match

Many operations return a Result . To pass an error up to your caller, you can match on it and return Err(e) on failure. This works, but it's noisy — especially when several steps can each fail.

Every fallible step needs its own four-line match . Imagine doing this five times in one function — the real logic gets buried. Rust has a much cleaner tool.

2️⃣ The ? Operator

The ? operator does exactly what that match did, in one character. On an Ok it unwraps the value; on an Err it returns that error from the function immediately. The only requirement is that the enclosing function returns a Result (or Option ).

In sum_two , if the first parse fails the function returns right there and never touches the second — short-circuiting on the first error. That's the power of ? : clean, linear code that still handles every failure.

Your turn. Fill in the blanks marked ___ , then run it.

Parse a slice of strings to numbers with ? and compute their average, returning a Result . Run it with cargo run .

📋 Quick Reference — Error Handling

Practice quiz

How does Rust handle recoverable errors?

  • With exceptions
  • With null returns
  • With the Result type
  • By aborting

Answer: With the Result type. Rust uses Result<T, E> for recoverable errors rather than exceptions.

On an Ok value, what does the ? operator do?

  • Unwraps the value and continues
  • Returns early
  • Panics
  • Logs the value

Answer: Unwraps the value and continues. On Ok(value), ? evaluates to value and execution continues.

On an Err, what does the ? operator do?

  • Ignores it
  • Retries the operation
  • Converts it to Ok
  • Immediately returns the Err from the enclosing function

Answer: Immediately returns the Err from the enclosing function. On Err(e), ? returns Err(e) early from the current function.

Why must a function using ? return a Result (or Option)?

  • For speed
  • Because ? returns the error out of the function, which needs a Result return type to hold it
  • Because ? prints errors
  • It doesn't have to

Answer: Because ? returns the error out of the function, which needs a Result return type to hold it. ? short-circuits by returning the error, so there must be somewhere (a Result) for that Err to go.

What does ? replace compared to manual error handling?

  • A verbose match that returns Err(e) early
  • A loop
  • A panic
  • An if statement

Answer: A verbose match that returns Err(e) early. ? collapses a four-line match-and-return-early into a single character.

In multiply(a, b) using ?, what does multiply("6", "7") return?

  • 42
  • Err(..)
  • Ok(42)
  • 13

Answer: Ok(42). Both parse successfully, so the function returns Ok(42) (6 * 7 wrapped in Ok).

In sum_two where the first parse fails, what happens to the second parse?

  • It still runs
  • It is never reached — ? short-circuits on the first error
  • It panics
  • It returns Ok

Answer: It is never reached — ? short-circuits on the first error. ? returns on the first error, so execution never touches the second parse.

Can ? convert between different error types?

  • No
  • Only for strings
  • Only inside main
  • Yes, it calls .into() via the From trait when a conversion exists

Answer: Yes, it calls .into() via the From trait when a conversion exists. When the error type differs, ? auto-converts using From, which is why Box<dyn Error> works so smoothly.

Which is the idiomatic choice to propagate an error to the caller?

  • unwrap
  • the ? operator
  • panic!
  • expect

Answer: the ? operator. Use ? to propagate; use match/if let to handle locally; reserve unwrap/expect for impossible cases or prototypes.

For average(values) that parses each &str with ?, what does average(&["10", "oops"]) return?

  • Ok(5.0)
  • Ok(20.0)
  • Err(ParseIntError ..)
  • 0.0

Answer: Err(ParseIntError ..). "oops" fails to parse, so ? returns the Err immediately and the function yields Err(ParseIntError).