Module 6 • Lesson 34

Error Handling Patterns

📚 9 min💻 Free🦀 nixus.pro

Error Handling Patterns in Practice

use std::error::Error;

// Pattern 1: Libraries use typed errors, apps use anyhow/Box
type AppResult = Result>;

// Pattern 2: Early return with ?
fn process_data(input: &str) -> AppResult> {
    let numbers: Result, _> = input
        .lines()
        .filter(|l| !l.trim().is_empty())
        .map(|l| l.trim().parse::())
        .collect();
    Ok(numbers?)
}

// Pattern 3: Error context (with anyhow)
// use anyhow::{Context, Result};
// fn read_config(path: &str) -> Result {
//     let content = fs::read_to_string(path)
//         .with_context(|| format!("Failed to read config from {}", path))?;
//     Ok(content.parse()?)
// }

// Pattern 4: Converting errors at boundaries
fn convert_and_log(result: Result) -> Option {
    result.map_err(|e| eprintln!("Warning: {}", e)).ok()
}

// Pattern 5: Combining multiple Results
fn multi_parse(a: &str, b: &str, c: &str) -> Result<(i32, f64, bool), String> {
    let x = a.parse::().map_err(|e| e.to_string())?;
    let y = b.parse::().map_err(|e| e.to_string())?;
    let z = c.parse::().map_err(|e| e.to_string())?;
    Ok((x, y, z))
}

fn main() {
    println!("{:?}", process_data("1
2
3
4
5"));
    println!("{:?}", multi_parse("42", "3.14", "true"));
    println!("{:?}", multi_parse("oops", "3.14", "true")); // Err on first fail
}

🎯 Practice

  1. Implement a mini CSV parser that returns descriptive errors with line numbers
  2. Write fn retry<T, E>(attempts: u32, f: impl Fn() -> Result<T, E>) -> Result<T, E>
  3. Add context to errors using map_err to include file names and line numbers in error messages

🎉 Key Takeaways