capypad
0 day streak
rust / intermediate
Snippet

Custom Error Types with Result<T, E>

Result<T, E> is Rust's primary error handling mechanism. By defining custom error enums, you can represent multiple failure modes with domain-specific information. Implementing the Display trait allows human-readable error messages. The From trait enables automatic error conversion with the ? operator, chaining operations that may fail. This approach composes well and makes error paths explicit in the type system.

snippet.rs
rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
use std::fmt;
use std::num::ParseIntError;
 
#[derive(Debug)]
enum ValidationError {
EmptyString,
TooShort { min: usize, actual: usize },
ParseError(ParseIntError),
OutOfRange { min: i32, max: i32, actual: i32 },
}
 
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ValidationError::EmptyString => write!(f, "String cannot be empty"),
ValidationError::TooShort { min, actual } =>
write!(f, "String too short: min={}, actual={}", min, actual),
ValidationError::ParseError(e) => write!(f, "Parse error: {}", e),
ValidationError::OutOfRange { min, max, actual } =>
write!(f, "Value {} out of range [{}, {}]", actual, min, max),
}
}
}
 
impl From<ParseIntError> for ValidationError {
fn from(err: ParseIntError) -> Self {
ValidationError::ParseError(err)
}
}
 
fn validate_age(input: &str) -> Result<i32, ValidationError> {
if input.is_empty() {
return Err(ValidationError::EmptyString);
}
if input.len() < 2 {
return Err(ValidationError::TooShort { min: 2, actual: input.len() });
}
let age: i32 = input.parse()?;
if age < 0 || age > 150 {
return Err(ValidationError::OutOfRange { min: 0, max: 150, actual: age });
}
Ok(age)
}
 
fn main() {
let test_cases = vec!["", "a", "25", "200", "-5"];
for input in test_cases {
match validate_age(input) {
Ok(age) => println!("Valid age: {}", age),
Err(e) => println!("Error for '{}': {}", input, e),
}
}
}
Breakdown
1
enum ValidationError { ... }
Custom error enum with variants for different failure scenarios
2
impl fmt::Display for ValidationError
Implementing Display for user-facing error messages
3
impl From<ParseIntError> for ValidationError
From implementation enables ? operator to convert parse errors automatically
4
fn validate_age(input: &str) -> Result<i32, ValidationError>
Function returning Result type with custom error enum
5
let age: i32 = input.parse()?;
? operator propagates ParseIntError converted to ValidationError
6
match validate_age(input) { ... }
Match on Result to handle success and error cases explicitly