TL;DR
- No exceptions. Use
Result<T, E> for recoverable, panic! for unrecoverable.
? operator propagates errors up the call chain.
- Use
thiserror for library errors, anyhow for application errors.
The Basics
use std::fs;
// Returns Result — caller decides what to do
fn read_config(path: &str) -> Result<String, std::io::Error> {
fs::read_to_string(path)
}
// ? propagates error to caller
fn get_port() -> Result<u16, Box<dyn std::error::Error>> {
let config = fs::read_to_string("config.toml")?;
let port: u16 = config.trim().parse()?;
Ok(port)
}
Cheat Sheet
| Want to... |
Use |
| Crash immediately |
panic!("msg") / .unwrap() |
| Provide default |
.unwrap_or(default) / `.unwrap_or_else( |
| Propagate error |
? operator |
| Transform error |
`.map_err( |
| Custom error type |
thiserror::Error derive |
| Quick prototyping |
anyhow::Result<T> |
Custom Errors (thiserror)
use thiserror::Error;
#[derive(Error, Debug)]
enum AppError {
#[error("not found: {0}")]
NotFound(String),
#[error("io error")]
Io(#[from] std::io::Error),
#[error("parse error")]
Parse(#[from] std::num::ParseIntError),
}