Rust groups errors into two major categories:

For a recoverable error, such as a file not found error, we most likely just want to report the problem to the user and retry the operation.

Unrecoverable errors are always symptoms of bugs, like trying to access a location beyond the end of an array, and so we want to immediately stop the program.

<aside> 💡 Rust doesn’t have exceptions. Instead, it has the type Result<T, E> for recoverable errors and panic! macro that stops execution when the program encounters an unrecoverable error.

</aside>



Unrecoverable Errors with panic!

Sometimes, bad things happen in your code, and there’s nothing you can do about it. In these cases, Rust has the panic! macro. There are two ways to cause a panic in practice: by taking an action that causes our code to panic (such as accessing an array past the end) or by explicitly calling the panic! macro.

<aside> 💡 By default, these panics will print a failure message, unwind, clean up the stack, and quit.

</aside>

Unwinding the Stack or Aborting in Response to a Panic

By default, when a panic occurs, the program starts unwinding, which means Rust walks back up the stack and cleans up the data from each function it encounters. However, this walking back and cleanup is a lot of work. Rust, therefore, allows you to choose the alternative of immediately aborting, which ends the program without cleaning up.

Memory that the program was using will then need to be cleaned up by the operating system. If in your project you need to make the resulting binary as small as possible, you can switch from unwinding to aborting upon a panic by adding panic = 'abort' to the appropriate [profile] sections in your Cargo.toml file. For example, if you want to abort on panic in release mode, add this:


[profile.release]
panic = 'abort'


fn main() {
    panic!("crash and burn");
}

Using a panic! Backtrace


fn main() {
    let v = vec![1, 2, 3];

    v[99];
}

<aside> 💡 In C, attempting to read beyond the end of a data structure is undefined behavior. You might get whatever is at the location in memory that would correspond to that element in the data structure, even though the memory doesn’t belong to that structure. This is called a *buffer overread* and can lead to security vulnerabilities if an attacker is able to manipulate the index in such a way as to read data they shouldn’t be allowed to that is stored after the data structure.

</aside>

⇒ A backtrace is a list of all the functions that have been called to get to this point. Backtraces in Rust work as they do in other languages: the key to reading the backtrace is to start from the top and read until you see the files you wrote. That’s the spot where the problem originated.