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).