Skip to content

Error Handling

Rust provides robust mechanisms for handling errors through enums like Result and Option, emphasizing safety and clarity.


panic!() Macro

The panic!() macro immediately terminates the program and outputs an error message. Use it sparingly, only in unrecoverable scenarios or during prototyping.

panic!("crash and burn");

Debugging with Backtrace

Set the environment variable RUST_BACKTRACE=1 to display a backtrace when a panic! occurs.

Result Enum

The Result enum is used for functions that can succeed or fail. It has two variants: - Ok(T) — success, containing a value. - Err(E) — error, containing an error value.

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

Handling Result with match

Use match to explicitly handle both Ok and Err.

use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let f = File::open("hello.txt");

    let f = match f {
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("Problem creating the file: {:?}", e),
            },
            other_error => panic!("Problem opening the file: {:?}", other_error),
        },
    };
}

Error Propagation

Error propagation allows errors to bubble up to the caller. You can manually propagate errors using match:

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

fn read_username_from_file() -> Result<String, io::Error> {
    let f = File::open("hello.txt");

    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut s = String::new();

    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

The ? Operator

The ? operator simplifies error propagation by automatically returning errors. It works only in functions that return Result or Option.

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")?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

Chaining with ?

You can further simplify the code by chaining method calls.

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

unwrap and expect

These methods are convenient but should be used sparingly, as they cause the program to panic on errors.

Examples

// Returns the inner value if Some, panics if None
Option::unwrap();

// Returns the inner value if Ok, panics if Err
Result::unwrap();

// Returns the inner value if Some, or a default if None
Option::unwrap_or(default);

// Returns the inner value if Ok, or a default if Err
Result::unwrap_or(default);

// Returns the inner value if Ok, or computes a value from a closure if Err
Result::unwrap_or_else(|err| handle_err(err));

// Returns the inner value if Some, panics with a custom error message if None
Option::expect("Custom panic message");

// Returns the inner value if Ok, panics with a custom error message if Err
Result::expect("Custom panic message");

Best Practice

Avoid using unwrap and expect in production code. Prefer safer alternatives like unwrap_or, unwrap_or_else, or proper error handling with match.

Conversion Between Result and Option

ok() (Result → Option)

Converts a Result<T, E> into an Option<T>, discarding the error.

let opt: Option<i32> = Result::Ok(10).ok(); // Some(10)

ok_or(err) (Option → Result)

Converts an Option<T> into a Result<T, E>, using a provided error.

let res: Result<i32, &str> = Some(10).ok_or("Missing value"); // Ok(10)

Transforming Errors with map_err

The map_err method transforms the error (E) in a Result into another type while retaining the Err variant.

Using match

let res: Result<i32, &str> = Ok(10);

match res {
    Ok(val) => println!("Value: {}", val),
    Err(e) => println!("Error: {}", e),
}

Using if let

let res: Result<i32, &str> = Ok(10);

if let Ok(val) = res {
    println!("Value: {}", val);
}

Printing Errors with eprint!

Use eprint! or eprintln! to print error messages directly to standard error (stderr).

eprint!("An error occurred: {}", error);
eprintln!("An error occurred: {}", error);