Skip to content

01. Robust Error Handling

Rust handles potential failures through the type system rather than exceptions, making your code safer and more predictable.

1. The Option<T> Enum

The Option type is used when a value might be absent. It replaces the “null” pointer concept found in other languages.

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

fn find_item(id: i32) -> Option<String> {
    if id == 1 { Some(String::from("ItemFound")) } else { None }
}

2. The Result<T, E> Enum

The Result type is used for functions that can fail. It returns either the successful value or an error.

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

fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Cannot divide by zero"))
    } else {
        Ok(a / b)
    }
}

3. Propagating Errors with ?

The ? operator is a shorthand for returning early if an error occurs. It makes error propagation concise.

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

fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?; // If error, returns it immediately
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

4. Unrecoverable Errors with panic!

If a program reaches a state it cannot handle, use panic!. This stops execution and cleans up the stack (unwinding).

fn get_critical_value() {
    panic!("Critical system failure!");
}

When to Panic?

  • Prototypes/Examples: Panicking is okay for speed.
  • Contract Violations: If an input violates the fundamental assumptions of a function.
  • Unrecoverable Situations: If the program cannot continue in any safe way.

Error handling prepares us for the more complex abstractions in Module 04: Generics, Traits, and Lifetimes.