Chapter 9 - Error Handling

  • Rust groups errors into two major categories: recoverable and unrecoverable errors.
  • Rust doesn’t have exceptions. Instead, it has the type Result<T, E> for recoverable errors and the panic! macro that stops execution when the program encounters an unrecoverable error.

panic! macro

When the panic! macro executes, your program will print a failure message, unwind and clean up the stack, and then quit.

But this walking back and cleanup is a lot of work. The alternative is to immediately abort, which ends the program without cleaning up. For example, if you want to abort on panic in release mode, add this to cargol.toml:

[profile.release]
panic = 'abort'

Recoverable errors with Result


#![allow(unused)]
fn main() {
enum Result<T, E> {
    Ok(T),
    Err(E),
}
}

Shortcuts for Panic on Error: unwrap and expect

If the Result value is the Ok variant, unwrap will return the value inside the Ok. If the Result is the Err variant, unwrap will call the panic! macro for us:

use std::fs::File;

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

Another method, expect, which is similar to unwrap, lets us also choose the panic! error message. Using expect instead of unwrap and providing good error messages can convey your intent and make tracking down the source of a panic easier. The syntax of expect looks like this:

use std::fs::File;

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

Propagating errors


#![allow(unused)]
fn main() {
use std::io;
use std::io::Read;
use std::fs::File;

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 above code can be written as:


#![allow(unused)]
fn main() {
use std::io;
use std::io::Read;
use std::fs::File;

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)
}
}

There is a difference between what the match expression and what ? operator do: error values that have the ? operator called on them go through the from function, defined in the From trait in the standard library, which is used to convert errors from one type into another.

Note that the ? operator can only be used in functions that have a return type of Result.