Custom errors — implement Error trait or use thiserror crate.
Using thiserror:
1use thiserror::Error;23#[derive(Error, Debug)]4enum AppError {5 #[error("Database error: {0}")] Db(#[from] sqlx::Error),6 #[error("Not found: {0}")] NotFound(String),7 #[error("Validation failed")] Validation(#[source] ValidationError),8}
Manual implementation:
1use std::fmt;2use std::error::Error;34#[derive(Debug)]5enum AppError {6 Io(std::io::Error),7 Parse(std::num::ParseIntError),8}910impl fmt::Display for AppError {11 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {12 match self {13 Self::Io(e) => write!(f, "IO error: {}", e),14 Self::Parse(e) => write!(f, "Parse error: {}", e),15 }16 }17}1819impl Error for AppError {20 fn source(&self) -> Option<&(dyn Error + 'static)> {21 match self {22 Self::Io(e) => Some(e),23 Self::Parse(e) => Some(e),24 }25 }26}
Key: thiserror for derive, manual for control.