capypad
0 day streak
rust / intermediate
Snippet

The ? Operator: Elegant Error Propagation

The ? operator is syntactic sugar for error propagation that makes code concise and readable. Instead of manually matching on Result variants and returning errors, ? automatically unwraps Ok values and returns Err values early from the current function. Combined with the From trait, ? can even convert between error types automatically—ParseIntError automatically converts to MathError because we implemented From.

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
use std::num::ParseIntError;
use std::fmt;
 
#[derive(Debug)]
enum MathError {
DivisionByZero,
NegativeLogarithm,
ParseError(ParseIntError),
}
 
impl fmt::Display for MathError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
MathError::DivisionByZero => write!(f, "Division by zero"),
MathError::NegativeLogarithm => write!(f, "Logarithm of negative number"),
MathError::ParseError(ref e) => write!(f, "Parse error: {}", e),
}
}
}
 
impl From<ParseIntError> for MathError {
fn from(err: ParseIntError) -> Self {
MathError::ParseError(err)
}
}
 
fn parse_and_divide(a: &str, b: &str) -> Result<f64, MathError> {
let a_num: i32 = a.parse()?;
let b_num: i32 = b.parse()?;
if b_num == 0 { return Err(MathError::DivisionByZero); }
Ok(a_num as f64 / b_num as f64)
}
 
fn main() {
let result = parse_and_divide("10", "2");
println!("Result: {:?}", result);
}
Breakdown
1
impl From<ParseIntError> for MathError {
Enables automatic error type conversion when using ?
2
let a_num: i32 = a.parse()?;
parse() returns Result; ? unwraps Ok or returns Err early
3
if b_num == 0 { return Err(MathError::DivisionByZero); }
Explicit error return for domain-specific validation
4
Ok(a_num as f64 / b_num as f64)
Ok wrapper indicates successful computation