15.7 Best Practices
Effective error handling requires more than simply using Result
or calling panic!
. Below are general guidelines for writing robust, maintainable Rust code.
15.7.1 Return Errors to the Call Site
Whenever possible, return errors up the call stack. Let the caller decide whether to retry, log, or proceed with a fallback:
fn read_config_file() -> Result<Config, io::Error> {
let contents = std::fs::read_to_string("config.toml")?;
parse_config(&contents)
}
fn main() {
match read_config_file() {
Ok(config) => apply_config(config),
Err(e) => {
eprintln!("Failed to read config: {}", e);
apply_default_config();
}
}
}
15.7.2 Meaningful Error Messages
When transforming or returning errors, add context that helps diagnose issues:
fn read_file(path: &str) -> Result<String, String> {
std::fs::read_to_string(path)
.map_err(|e| format!("Error reading '{}': {}", path, e))
}
15.7.3 Use unwrap
and expect
Sparingly
The methods unwrap
and expect
are convenient for quick prototypes or tests but can lead to panics if something goes wrong. In production code, prefer explicit error handling unless you are absolutely sure a failure is impossible:
let content = std::fs::read_to_string("config.toml")
.expect("Unable to read config.toml; please check the file path!");