error-handling

Master Rust's Result, Option, and error handling patterns. Use when implementing error types, propagation with ?, or choosing between thiserror and anyhow.

SKILL.md

GitHub

Error Handling Skill

Master Rust's explicit, type-safe error handling with Result and Option.

Quick Start

Option - For Absent Values

fn find_user(id: u32) -> Option<User> {
    if id == 0 {
        None
    } else {
        Some(User { id, name: "Alice".into() })
    }
}

// Handling Option
match find_user(1) {
    Some(user) => println!("Found: {}", user.name),
    None => println!("User not found"),
}

// Or with if let
if let Some(user) = find_user(1) {
    println!("Found: {}", user.name);
}

Result - For Recoverable Errors

use std::fs::File;
use std::io::{self, Read};

fn read_file(path: &str) -> Result<String, io::Error> {
    let mut file = File::open(path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

// Handling Result
match read_file("data.txt") {
    Ok(contents) => println!("{}", contents),
    Err(e) => eprintln!("Error: {}", e),
}

The ? Operator

fn process_data(path: &str) -> Result<ProcessedData, AppError> {
    let contents = read_file(path)?;     // Returns Err if fails
    let parsed = parse(&contents)?;       // Returns Err if fails
    let validated = validate(parsed)?;    // Returns Err if fails
    Ok(validated)
}

Common Patterns

unwrap_or / unwrap_or_else

// Default value
let name = get_name().unwrap_or("Anonymous".to_string());

// Lazy default (computed only if needed)
let name = get_name().unwrap_or_else(|| generate_default_name());

map / and_then

// Transform the inner value
let doubled = Some(5).map(|x| x * 2); // Some(10)

// Chain operations that return Option/Result
let result = get_value()
    .and_then(|v| validate(v))
    .and_then(|v| process(v));

ok_or / ok_or_else

// Convert Option to Result
let user = find_user(id).ok_or(Error::NotFound)?;

// Lazy error
let user = find_user(id).ok_or_else(|| Error::NotFound(id))?;

Custom Errors with thiserror

use thiserror::Error;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("Parse error at line {line}: {message}")]
    Parse { line: usize, message: String },

    #[error("Not found: {0}")]
    NotFound(String),
}

Application Errors with anyhow

use anyhow::{Context, Result, bail, ensure};

fn process() -> Result<()> {
    let config = load_config()
        .context("Failed to load configuration")?;

    ensure!(config.is_valid(), "Configuration is invalid");

    if config.debug {
        bail!("Debug mode not allowed in production");
    }

    Ok(())
}

Resources