15.6 Best Practices
Simply using Result
or calling panic!
does not suffice for robust error handling. Thoughtful application of Rust’s mechanisms will result in maintainable, clear, and safe code.
15.6.1 Return Errors to the Call Site
Whenever possible, let the caller decide how to handle an error:
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.6.2 Provide Clear Error Messages
When transforming errors, include context to help debug problems:
fn read_file(path: &str) -> Result<String, String> {
std::fs::read_to_string(path)
.map_err(|e| format!("Error reading '{}': {}", path, e))
}
15.6.3 Use unwrap
and expect
Sparingly
While unwrap
or expect
are handy during prototyping or in test examples, avoid them in production code unless you are certain an error is impossible:
let content = std::fs::read_to_string("config.toml")
.expect("Unable to read config.toml; please check the file path!");
Overusing these methods can lead to unexpected panics at runtime, making debugging more difficult.