Custom Error Types
use std::fmt;
use std::num::ParseIntError;
// Simple custom error
#[derive(Debug)]
enum ConfigError {
NotFound(String),
ParseError(String),
InvalidValue { key: String, value: String, expected: String },
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ConfigError::NotFound(key) =>
write!(f, "Config key '{}' not found", key),
ConfigError::ParseError(msg) =>
write!(f, "Parse error: {}", msg),
ConfigError::InvalidValue { key, value, expected } =>
write!(f, "Invalid value '{}' for '{}', expected {}", value, key, expected),
}
}
}
impl std::error::Error for ConfigError {}
// Conversions
impl From for ConfigError {
fn from(e: ParseIntError) -> Self {
ConfigError::ParseError(e.to_string())
}
}
fn parse_port(s: &str) -> Result {
let n: u16 = s.trim().parse()?; // ParseIntError -> ConfigError
if n < 1024 {
return Err(ConfigError::InvalidValue {
key: "port".to_string(),
value: s.to_string(),
expected: ">= 1024".to_string(),
});
}
Ok(n)
}
fn main() {
match parse_port("80") {
Ok(p) => println!("Port: {}", p),
Err(e) => println!("Error: {}", e), // Uses Display
}
match parse_port("8080") {
Ok(p) => println!("Port: {}", p),
Err(e) => eprintln!("Error: {}", e),
}
} Using thiserror (Recommended Crate)
// Cargo.toml: thiserror = "1"
use thiserror::Error;
#[derive(Debug, Error)]
enum DatabaseError {
#[error("Connection failed: {0}")]
ConnectionFailed(String),
#[error("Query error on table '{table}': {message}")]
QueryError { table: String, message: String },
#[error("Record not found: id={id}")]
NotFound { id: u64 },
#[error("IO error")]
Io(#[from] std::io::Error),
}
fn find_user(id: u64) -> Result {
if id == 0 {
return Err(DatabaseError::NotFound { id });
}
Ok(format!("user_{}", id))
}
fn main() {
println!("{}", find_user(0).unwrap_err()); // "Record not found: id=0"
} 🎯 Practice
- Create a ValidationError enum with variants for different validation failures
- Implement Display and Error traits manually (without thiserror)
- Add a From<io::Error> conversion so ? works in functions returning ValidationError
🎉 Key Takeaways
- Custom errors need Debug, Display (fmt), and std::error::Error impls
- Implement From<OtherError> to enable ? conversion
- thiserror crate generates boilerplate via #[derive(Error)] - use in libraries
- anyhow crate is great for applications where you just want Box<dyn Error>