Introduction
Rust has no try/catch and no exceptions. Instead, recoverable errors are represented as values using the Result<T, E> enum, which forces the caller to explicitly handle both the success and failure cases at compile time. This makes error paths visible in function signatures rather than hidden as control-flow jumps.
Cricket analogy: Rust has no umpire-review exception system; instead every risky run attempt returns a Result — Out or NotOut — forcing the batting captain to check the outcome explicitly rather than assume safety.
Syntax
enum Result<T, E> {
Ok(T),
Err(E),
}
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
return Err(String::from("division by zero"));
}
Ok(a / b)
}Explanation
A function that can fail returns Result<T, E>, where T is the success type and E is the error type. Callers use match, if let, or combinators like .map() and .unwrap_or() to handle both variants. The ? operator can be placed after an expression that returns a Result: if the value is Err, the function returns immediately with that Err; if it is Ok, the inner value is unwrapped and execution continues. The ? operator only works inside functions that themselves return a Result (or Option) type.
Cricket analogy: A run-chase function returning Result<Runs, String> is handled with match or .unwrap_or(0); the ? operator is like a captain who, on a Err(rain_stopped), immediately ends the innings report rather than continuing.
Example
use std::num::ParseIntError;
fn parse_and_double(input: &str) -> Result<i32, ParseIntError> {
let n: i32 = input.parse()?; // propagate error early
Ok(n * 2)
}
fn main() {
match parse_and_double("21") {
Ok(value) => println!("Doubled: {}", value),
Err(e) => println!("Failed to parse: {}", e),
}
match parse_and_double("abc") {
Ok(value) => println!("Doubled: {}", value),
Err(e) => println!("Failed to parse: {}", e),
}
}Output
Doubled: 42
Failed to parse: invalid digit found in stringKey Takeaways
- Result<T, E> = Ok(T) | Err(E) is Rust's mechanism for recoverable errors — there are no exceptions.
- The ? operator propagates an Err up the call stack and unwraps Ok values, but only inside functions returning Result or Option.
- .unwrap() and .expect("msg") panic on Err and are best reserved for prototypes, tests, or truly unrecoverable states.
- Error handling is explicit and checked by the compiler, so failure paths cannot be silently ignored.
- Combinators like .map(), .map_err(), .and_then(), and .unwrap_or() let you transform Results without manual match blocks.
Practice what you learned
1. What does the ? operator do when applied to an Err value inside a function returning Result?
2. Which two variants make up the Result<T, E> enum?
3. Where can the ? operator be used?
4. What happens when .unwrap() is called on an Err value?
5. What does Rust use instead of try/catch for recoverable errors?
Was this page helpful?
You May Also Like
The Option Type in Rust
Understand how Rust's Option<T> enum replaces null to represent optional values safely.
panic! and Unwinding in Rust
Learn how panic! signals unrecoverable errors in Rust, how stack unwinding works, and when to use it versus Result.
Enums in Rust
Enums define a type by enumerating its possible variants, each of which can optionally hold its own data.
match Expressions in Rust
Master Rust's exhaustive match expression for pattern matching on values, enums, tuples, and ranges.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics