The ? Operator: Ergonomic Error Propagation
The ? operator is syntactic sugar for early return on error. It is the most ergonomic way to handle errors in Rust.
use std::fs;
use std::num::ParseIntError;
use std::io;
// Without ?: verbose
fn read_and_parse_verbose(path: &str) -> Result {
let content = match fs::read_to_string(path) {
Ok(s) => s,
Err(e) => return Err(e),
};
// But we can not parse here without error type mismatch...
Ok(42)
}
// With ?: clean and readable
fn read_and_parse(path: &str) -> Result> {
let content = fs::read_to_string(path)?; // Returns Err if file read fails
let n: i32 = content.trim().parse()?; // Returns Err if parse fails
Ok(n * 2)
}
// ? also works with Option
fn first_even(v: &[i32]) -> Option {
let first = v.first()?; // Returns None if empty
if first % 2 == 0 { Some(*first) } else { None }
}
// ? works in main() too (requires main to return Result)
fn main() -> Result<(), Box> {
// let n = read_and_parse("numbers.txt")?;
println!("Success!");
Ok(()) // main returns Ok(())
} The From Trait and Error Conversion
use std::num::ParseIntError;
use std::io;
#[derive(Debug)]
enum AppError {
Io(io::Error),
Parse(ParseIntError),
}
impl From for AppError {
fn from(e: io::Error) -> Self { AppError::Io(e) }
}
impl From for AppError {
fn from(e: ParseIntError) -> Self { AppError::Parse(e) }
}
// Now ? auto-converts these errors!
fn process_file(path: &str) -> Result {
let content = std::fs::read_to_string(path)?; // io::Error -> AppError via From
let n: i32 = content.trim().parse()?; // ParseIntError -> AppError via From
Ok(n)
} 🎯 Practice
- Write a function that reads a CSV file and returns Vec<Vec<String>>, using ? throughout
- Create an AppError enum and impl From for two different error types
- Write fn main() -> Result<(), Box<dyn Error>> that reads a file and parses JSON-ish data
🎉 Key Takeaways
- ? is sugar for: if Err(e) = result { return Err(e.into()) }
- It calls .into() on the error, enabling automatic type conversion via From
- Works in functions returning Result or Option
- main() can return Result<(), Box<dyn Error>> for top-level ? usage