blog.dopana

Back

Tenth post in the basic Rust series. Rust groups errors into two major categories: unrecoverable errors with panic! and recoverable errors with Result<T, E>.

Unrecoverable errors with panic!#

When a program encounters a fatal condition (such as an out-of-bounds array access), Rust executes the panic! macro. This prints a failure message, unwinds the stack, and exits the process:

fn main() {
    // panic!("crash and burn");
    let v = vec![1, 2, 3];
    // v[99]; // panics automatically
}
rust

Use panic! when an invalid state occurs that your program cannot recover from.

Recoverable errors with Result#

For operations that can reasonably fail (such as opening a file or parsing an integer), functions return Result<T, E>:

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

You inspect Result using match:

use std::fs::File;

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

    let _greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => panic!("Problem opening the file: {error:?}"),
    };
}
rust

Shortcuts: unwrap and expect#

Result provides convenience methods to extract T or panic on Err:

let file = File::open("hello.txt").unwrap();
let file = File::open("hello.txt").expect("hello.txt should exist in this directory");
rust

Prefer expect over unwrap in production code because it provides useful context in crash logs.

Propagating errors with ?#

When writing functions, you often want to return errors up to the caller rather than handling them immediately. The ? operator provides a clean, concise syntax for error propagation:

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

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

fn main() -> Result<(), io::Error> {
    let username = read_username_from_file()?;
    println!("Username: {username}");
    Ok(())
}
rust

If an operation evaluated with ? returns an Err, that Err is immediately returned from the enclosing function.

Conclusion#

Rust makes error handling explicit: panic! handles unrecoverable failures, Result<T, E> manages recoverable errors, and ? allows ergonomic propagation. This completes the 10-part basic Rust series!

References#