15.7 Best Practices
15.7.1 Returning Errors to the Call Site
It's often better to return errors to the call site rather than handling them immediately within a function. This approach:
- Provides Flexibility: Allows the caller to decide how to handle the error, whether to retry, log, or propagate it further.
- Simplifies Functions: Keeps functions focused on their primary task without being cluttered with error-handling logic.
- Encourages Reusability: Functions that return
Result
can be reused in different contexts with varying error-handling strategies.
Example:
use std::io; fn read_config_file() -> Result<Config, io::Error> { let contents = std::fs::read_to_string("config.toml")?; parse_config(&contents) } fn main() { // Ensure all possible error cases are handled, providing meaningful responses or recovery strategies. match read_config_file() { Ok(config) => apply_config(config), Err(e) => { eprintln!("Failed to read config: {}", e); // Decide how to handle the error here apply_default_config(); } } }
15.7.2 Meaningful Error Messages
Provide clear and informative error messages to aid in debugging and user understanding.
Example:
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 Cautious Use of unwrap
and expect
Avoid using unwrap
and expect
unless you are certain that a value is present.
-
Risky:
let content = std::fs::read_to_string("config.toml").unwrap();
-
Safer Alternative:
let content = std::fs::read_to_string("config.toml") .expect("Failed to read config.toml. Please ensure the file exists.");
-
Best Practice:
match std::fs::read_to_string("config.toml") { Ok(content) => { // Use content } Err(e) => eprintln!("Error: {}", e), }
By handling errors explicitly, you enhance program stability and user experience.