Rust
Beginner
1 min read
Custom Error Types and the thiserror Crate
Example
// Cargo.toml dependency: thiserror = "1"
// (Shown here using manual implementations for zero-dependency demo)
use std::fmt;
use std::num::ParseIntError;
#[derive(Debug)]
enum ConfigError {
Io(std::io::Error),
Parse(ParseIntError),
InvalidRange { value: i64, min: i64, max: i64 },
MissingField(String),
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConfigError::Io(e) => write!(f, "IO error: {e}"),
ConfigError::Parse(e) => write!(f, "parse error: {e}"),
ConfigError::InvalidRange { value, min, max } =>
write!(f, "value {value} is outside range [{min}, {max}]"),
ConfigError::MissingField(field) =>
write!(f, "missing required field: {field}"),
}
}
}
impl std::error::Error for ConfigError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ConfigError::Io(e) => Some(e),
ConfigError::Parse(e) => Some(e),
_ => None,
}
}
}
impl From<std::io::Error> for ConfigError { fn from(e: std::io::Error) -> Self { ConfigError::Io(e) } }
impl From<ParseIntError> for ConfigError { fn from(e: ParseIntError) -> Self { ConfigError::Parse(e) } }
fn parse_port(s: Option<&str>) -> Result<u16, ConfigError> {
let s = s.ok_or_else(|| ConfigError::MissingField("port".into()))?;
let n: i64 = s.parse()?;
if !(1..=65535).contains(&n) {
return Err(ConfigError::InvalidRange { value: n, min: 1, max: 65535 });
}
Ok(n as u16)
}
fn main() {
for input in [Some("8080"), Some("99999"), Some("abc"), None] {
match parse_port(input) {
Ok(p) => println!("port = {p}"),
Err(e) => println!("error: {e}"),
}
}
}