Error Handling

Rust exception basic: There is no exception in Rust, and there are two kinds of errors: recoverable and unrecoverable errors, instead. For recoverable errors, Rust gives the type Result<T, E> to it, and for unrecoverable errors, there is a panic! macro.

Unrecoverable Errors

Fatal errors cannot be recovered by the program itself so the whole program just panics. Whenever the panic! macro executes, the program will leave an error message, unwind and clean up the stack, and then quit with error code. By default, when a panic occurs, the program starts to unwinding, which means Rust walks back up the stack and cleans up the data from each function it encounters. We can also just abort the program by adding panic = 'abort' to the appropriate [profile] sections in the Cargo.toml file.

[profile.release]
panic = 'abort'

Recoverable Errors with Result

Most errors will not cause the entire program panic, so we might handle it or ignore it. We can use the enum called Result<T, E> to achieve this.

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

Here, T represents the type of the value that will be returned in a success case within the Ok variant, and E represents the type of the error that will be returned in a failure case within the Err variant. Because Result has these generic type parameters, we can use the Result type and the functions defined on it in many different situations where the successful value and error value we want to return may differ.

use std::fs::File;

let mut f = File::open("./data/input.txt");
// What if the file does not exist? The program has bug.
do_something_with_f(&mut f);

So the correct way to read the file should be as follows.

use std::fs::File;

let mut f = match File::open("./data/input.txt") {
	Ok(file) => file,
	Err(err) => println!("{:?}", err);
	// Or panic.
}

Matching on Different Types of Errors

Sometimes we need to execute different code snippets to handle different types of errors, and in this case we use std::io::ErrorKind to do us this favor.

use std::io::ErrorKind;

// Code snippet from previous.
let mut f = match File::open("./data/input.txt") {
  Ok(file) => file,
  Err(error) => match error.kind() {
    ErrorKind::NotFound => match File::create("./data/input.txt") {
      Ok(file) => file,
      Err(e) => panic!("{:?}", e),
    },
    other_error => {
      panic!("Unknown error! {:?}", other_error)
    }
  },
};

Shortcuts for Panic on Error: unwrap and expect

Using match expression might be verbose, so we use a more concise method called unwrap that automatically returns the value inside Ok and calls panic! if error happens. A very similar way is to use expect which gives us the error message.