Error Handling
The ? operator is shorthand for propagating a Result: if the expression is Err, ? returns that error immediately from the current function; if it's Ok, ? unwraps the value and execution continues. s.parse()? below either gives you a valid i32 or exits parse_and_double early with the parse error.
use std::num::ParseIntError;
fn parse_and_double(s: &str) -> Result<i32, ParseIntError> {
let n: i32 = s.parse()?;
Ok(n * 2)
}
fn main() {
match parse_and_double("21") {
Ok(n) => println!("Doubled: {}", n),
Err(e) => println!("Error: {}", e),
}
match parse_and_double("not a number") {
Ok(n) => println!("Doubled: {}", n),
Err(e) => println!("Error: {}", e),
}
}Doubled: 42 Error: invalid digit found in string
A custom error type
For your own functions, a custom error type — even a simple one like NegativeNumberError — is often clearer than reusing a generic error. Implementing the Display trait for it is what lets {} format it directly inside println!.
use std::fmt;
#[derive(Debug)]
struct NegativeNumberError;
impl fmt::Display for NegativeNumberError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "number must not be negative")
}
}
fn square_root_ish(n: i32) -> Result<f64, NegativeNumberError> {
if n < 0 {
return Err(NegativeNumberError);
}
Ok((n as f64).sqrt())
}
fn main() {
match square_root_ish(16) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
match square_root_ish(-4) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
}Result: 4 Error: number must not be negative
Next: collections and the standard library — HashMap, and a closing look at what else Rust's standard library provides.
Practice exercise
Write a function safe_divide(a: f64, b: f64) -> Result<f64, String> that returns Err("cannot divide by zero".to_string()) if b is 0.0, otherwise Ok(a / b). Call it with (10.0, 0.0), match the result, and print the error message if it's Err.