In this lesson, you will learn how to wrap lower-level errors in higher-level contexts to preserve diagnostic depth throughout your application.
The Problem: Context Loss
When an error bubbles up from a deep internal function (like a database connection failure), the caller often sees a generic error that lacks meaning. If your main function receives "File not found," it doesn't know if that file was a configuration file, a user upload, or a cache entry. Simply propagating the error with the ? operator often discards the "why" behind the failure.
The Pattern: Wrapping Errors
"Error Wrapping" is the practice of capturing a low-level error and bundling it inside a new, domain-specific error variant. This creates a "breadcrumb trail" of what went wrong. Instead of returning the raw underlying error, you transform it into a meaningful context.
Consider this example where we wrap an I/O error into a higher-level "Configuration" error:
enum AppError {
ConfigLoadError(String, std::io::Error),
}
fn load_config() -> Result<String, AppError> {
std::fs::read_to_string("config.toml").map_err(|e| {
// We wrap the raw io::Error inside our domain-specific variant
AppError::ConfigLoadError("Failed to read critical config file".to_string(), e)
})
}
By doing this, we keep the original std::io::Error preserved as the "source" of the problem, while adding the descriptive string "Failed to read critical config file" as the "context."
Why This Matters
When you eventually report this error to a user or a log file, you can inspect both layers. You aren't just telling the user that something failed; you are telling them where in your logic the failure occurred. This is the difference between a frustrating "Unknown Error" and a clear "Configuration failure: The file was locked by another process."
By preserving the lower-level error inside the higher-level type, you ensure that no diagnostic information is lost as the error travels up the stack.
Key Takeaway: Error wrapping preserves the specific cause of a failure while providing the high-level context necessary for effective debugging.