Custom Error Types
Library crates define error enums wrapping underlying failures, implement std::error::Error, Display, and From for ergonomic ?.
Recipe
use std::fmt;
#[derive(Debug)]
enum DataError {
Io(std::io::Error),
Parse(std::num::ParseIntError),
}
impl fmt::Display for DataError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DataError::Io(e) => write!(f, "io: {e}"),
DataError::Parse(e) => write!(f, "parse: {e}"),
}
}
}
impl std::error::Error for DataError {}
impl From<std::io::Error> for DataError {
fn from(e: std::io::Error) -> Self { DataError::Io(e) }
}When to reach for this: Public libraries exposing stable error types consumers can match.
Working Example
fn load_port(path: &str) -> Result<u16, DataError> {
let s = std::fs::read_to_string(path)?;
let p = s.trim().parse()?;
Ok(p)
}With From for ParseIntError added similarly.
What this demonstrates:
- Single enum aggregates error sources
?converts automatically- Callers can
matchonDataErrorvariants
Deep Dive
Implement source() on Error to chain causes. Use #[non_exhaustive] on public error enums for semver.
Gotchas
- String errors only - Hard to match programmatically. Fix: Typed enum variants.
- Missing
From-?fails. Fix: derive or implFrom. - Leaking internal errors - Breaks semver if exposed. Fix: Map to public enum.
- Huge error type - Clone cost. Fix:
Arcsource or boxed inner error. - No
Send/Sync- Breaks threads. Fix: Ensure wrapped errors areSend + Sync.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
thiserror | Derive boilerplate | Tiny internal crate |
anyhow | Application flexibility | Stable library API |
Box<dyn Error> | Quick binary | Typed matching needed |
FAQs
Error trait object?
`Boxsource() chain?
Walk `source()` for root cause logging.non_exhaustive?
Forward compatible public enums.flatten thiserror?
`#[from]` attribute generates From.HTTP mapping?
Implement `IntoResponse` in app layer not lib.serde errors?
Wrap `serde_json::Error` variant.no_std Error?
`core` error traits in no_std ecosystems.Clone errors?
Often wrap `Arc` or store static messages.Compare errors?
Avoid - match on kind enum instead.Testing?
Assert on error kind via `downcast` or match.Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.