Course contentsShow
← Rust
Lesson 786 of 1,9106. Generics and Static DispatchFree lesson

Generic Error Wrappers for Libraries

Create standardized error types that wrap arbitrary underlying errors for uniform library interfaces.

In this lesson, you will learn how to build a unified error interface for your library that hides implementation details while preserving the original error cause.

The Problem of Leaky Abstractions

When writing a library, your functions might encounter errors from various sources: network requests, file I/O, or database parsing. If you return these raw errors directly, your library users become tightly coupled to your specific implementation choices. If you swap your database driver later, your library's public API signature breaks, forcing users to rewrite their code.

The Generic Wrapper Solution

Instead, you can define a generic error wrapper that acts as a "container." By using a generic type parameter E, this wrapper can hold any error type that implements the standard std::error::Error trait.

use std::fmt;

pub struct LibraryError<E> {
    pub message: String,
    pub source: E,
}

impl<E: fmt::Display> fmt::Display for LibraryError<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}: {}", self.message, self.source)
    }
}

Why use this pattern?

By using this LibraryError<E> structure, your library's functions can return a consistent type while keeping the underlying cause dynamic. This is particularly powerful when you want to provide "Contextual Logic" (our previous lesson) alongside the error.

For example, you could write:

fn load_config<E>(path: &str) -> Result<String, LibraryError<E>> 
where E: std::error::Error 
{
    // If an error happens, wrap it:
    // Err(LibraryError { message: "Failed to read".into(), source: io_err })
}

This pattern ensures that your library’s public API remains stable. You can change the internal implementation entirely—or even swap out the underlying error types—without forcing your users to update their error-handling logic. It separates your library's "what went wrong" (the high-level message) from the specific "why it went wrong" (the wrapped generic error), creating a professional and maintainable surface for your code.

Key Takeaway: Use generic error wrappers to encapsulate internal implementation details, providing a stable, uniform error interface while preserving the full diagnostic information of the original cause.