01. Robust Error Handling
Rust handles potential failures through the type system rather than exceptions, making your code safer and more predictable.
1. The Option<T> Enum
The Option type is used when a value might be absent. It replaces the “null” pointer concept found in other languages.
enum Option<T> {
Some(T),
None,
}
fn find_item(id: i32) -> Option<String> {
if id == 1 { Some(String::from("ItemFound")) } else { None }
}2. The Result<T, E> Enum
The Result type is used for functions that can fail. It returns either the successful value or an error.
enum Result<T, E> {
Ok(T),
Err(E),
}
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("Cannot divide by zero"))
} else {
Ok(a / b)
}
}3. Propagating Errors with ?
The ? operator is a shorthand for returning early if an error occurs. It makes error propagation concise.
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?; // If error, returns it immediately
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}4. Unrecoverable Errors with panic!
If a program reaches a state it cannot handle, use panic!. This stops execution and cleans up the stack (unwinding).
fn get_critical_value() {
panic!("Critical system failure!");
}When to Panic?
- Prototypes/Examples: Panicking is okay for speed.
- Contract Violations: If an input violates the fundamental assumptions of a function.
- Unrecoverable Situations: If the program cannot continue in any safe way.
Error handling prepares us for the more complex abstractions in Module 04: Generics, Traits, and Lifetimes.