Centameta
← Back to Blog

Rust error handling: Option and Result

July 01, 2026

Rust turns many runtime errors into compile-time guarantees through two enums: Option<T> and Result<T, E>. Unlike null-pointer exceptions, you cannot ignore these values. The compiler forces you to handle them before using the underlying data.

Missing-value and failure cases become explicit types, not hidden assumptions.

Option<T>: a value that may not exist

enum Option<T> {
    Some(T),
    None,
}

Use Option when a value is optional.

let text = map.get("missing");
match text {
    Some(t) => println!("{}", t),
    None => println!("not found"),
}

Shorthand forms:

if let Some(t) = text {
    println!("{}", t);
}

let t = text.unwrap_or("default");

Result<T, E>: an operation that may fail

enum Result<T, E> {
    Ok(T),
    Err(E),
}

Use Result for operations that can fail with a typed reason.

match fs::read_to_string(path) {
    Ok(data) => process(data),
    Err(Error::NotFound) => eprintln!("missing file"),
    Err(Error::PermissionDenied) => eprintln!("access denied"),
    Err(e) => eprintln!("io failed: {}", e),
}

Pattern matching as try/catch

The closest equivalent to try/catch with per-error behavior is match on Result.

PatternPurpose
match result { ... }Exhaustive per-error handling
if let Err(e) = result { ... }Handle one variant
result.map_err(|e| handle(e))?Transform then propagate
? operatorReturn error to caller immediately
match parse_config(path) {
    Ok(cfg) => run(cfg),
    Err(ConfigError::Missing(key)) => eprintln!("missing key: {}", key),
    Err(ConfigError::Parse(e)) => eprintln!("parse failed: {}", e),
    Err(e) => return Err(e),
}

The ? operator

? unwraps Ok and returns Err early from the current function. It replaces nested match blocks for linear happy-path flow.

fn load() -> Result<Config, ConfigError> {
    let text = fs::read_to_string(path)?;
    let cfg = parse(&text)?;
    Ok(cfg)
}

Why not exceptions

Exceptions hide control flow. A function can return normally or throw from anywhere, making reasoning about state harder. Rust errors are values: they flow through the type system, are explicit in signatures, and cannot be silently ignored.

Core lesson: handle missing data with Option, handle recoverable failure with Result, and reserve panics for unrecoverable program invariants.