# Error Handing This is a factor of life in software, Rust has a number of features for handling errors. One feature is that Rust requires you to acknowledge the possibility of an error and take some action beofre our code will compile. This requirement ensures that errors are handled before the possiblity could arise This can be split into two major categories - [*Recoverable*](#recoverable-errors) - File a file not found, just need to report the problem to the user and retry the operation - [*Unrecoverable*](#unrecoverable-errors) - A symptom of bugs, like trying to access a location beyond the end of an array. Need to immediately stop the program Many languages dont distinguish between the two kinds of errors and handle them the same way using mechanisms such as exceptions Rust does not have exceptions Instead it has the type `Result< T, E>` for recoverable errors It has the `panc!` macro to stop eecution when an unrecoverable error occurs ## Unrecoverable Errors Whne bad things happen in your code and nothing you can do nothing about it then Rust has the `panc!` macro There are two ways to cause a panic: - by taking an action that causes the code to paic (like accessing an array past the end) - explicity calling `panic!` macro By default these print a failure message, unwind, clean up the stack and then quit. Using an environment variable you can also have Rust display the call stack when a panic occurs. This can make it easier to track down the source of the panic When a call to `panic!` occurs the error message will be contained in the last two lines. The first line will contain our message and the second is when te source of this panic occured example ```rust fn main() { panic!("crash and burn"); } ``` This will output ``` thread 'main' panicked at src/main.rs:2:5: crash and burn note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ``` This indicates that the panic occured in the file main.rs at the 2nd line on the 5th character In this example it indicates that it is part of our source cdoe, looking there will show the `panic!` macro In other cases the `panic!` call might be reported as someone else code where the `panic!` macro was called You can also use the traceback functions of the `panic` call to figure ot the part of our code that caused the problem To understand this an example will be used ```rust fn main() { let v = vec![1, 2, 3]; v[99]; } ``` Here we are tryin to access the 100th element, this is out of range and therefore Rust will initiate a error In C, attempting to read beyond hte end of a data structure is undefined behavior, and you might get whatever is at the memory location, this would be something "random" This is considered a *buffer overread* and can lead to security vulnerabilities, this would allow an attacker to be able to manipulate the index in such a way that they shouldnt be allowed to sore in that data structure. Rust protects yo from this kind of vulnerability by casuing a panic if you try to read something out of range. The `note:` line tells us that we can set the `RUST_BACKTRACE` environment variable to get a backtrace to show exactly what happened to casue the error. The key to reading a backtrace is to start at the top and read until you see the files you wrote, that is where the problem originates. The lines above that spot are code that our code has called, and the lines below are the code that called your code. These before-and-after lines might include core Rust code, std lib code or crates that you are using You can set the backtrace by setting the `RUST_BACKTRACE` environment variable to any value except 0 Example ``` RUST_BACKTRACE=1 cargo run thread 'main' panicked at src/main.rs:4:6: index out of bounds: the len is 3 but the index is 99 stack backtrace: 0: rust_begin_unwind at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5 1: core::panicking::panic_fmt at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14 2: core::panicking::panic_bounds_check at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:208:5 3: >::index at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/slice/index.rs:255:10 4: core::slice::index:: for [T]>::index at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/slice/index.rs:18:9 5: as core::ops::index::Index>::index at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/alloc/src/vec/mod.rs:2770:9 6: panic::main at ./src/main.rs:4:6 7: core::ops::function::FnOnce::call_once at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. ``` In order to a backtarace with this info, debug symbols must be enabled Debug symbols are enabled by defualt when using `cargo build` or `cargo run` without the `--release` flag In line 6 of the backtrace points to the line in our project that causes the problem, that would be line 4 of *src/main.rs* If we dont want our program to panc thne we sould start our investigation at the inidcated line we wrote ### Unwinding the Stack or Aborting in Response to a Panic *unwinding* in rust means that it walks back up the stack and cleans up the data form each function it encounters. However walking back and cleaning up is a lot of work Rust also allows yo to choose the alternative of immediately *aborting*, which means ends the program without cleaning up Memory that the program was using will thne be clean up by the OS If yo need your project's resultant binary as small as possible you can switch from unwinfing to aborting upon a panic This can be done by adding `panic = 'abort'` to the appropriate `[profile]` section in your Cargo.toml example of this ```toml [profile.release] panic = 'abort' ``` ## Recoverable Errors You can use the enum `Result` to handle most errors becasue they are not serious enough to warrant a panic One example of this of a non serious error is opening a file and that operation fails becasue that file doesnt exist, you may want to create the file instead of terminating the process the enum `Result` is defined as Which has two variants `Ok` and `Err` ```rust enum Result { Ok(T), Err(E), } ``` `T` and `E` are generic type parameters `T` reperesents the tpye of value that will be returned in a success case within th `Ok` variant `E` reperesents the tpye 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 success value and error value we want to reutrn may differ Lets use a function that retunrs a `Result` value because the function could fail ```rust use std::fs::File; fn main () { let greeting_file_result = File::open("hello.txt"); } ``` The return type of `File::open` is a `Result` The generic parameter `T` has been filled in by the implementation of `File::open` with the type of the success value is a file handle (`std::fs::File`) The `E` parameter is used in the error value which is `std::io::Error` This return type indicates that call may succeed and reutnr a file handle that we can read and write to or it may fail if it doesnt exist, or not having the correct permissions `File::open` function needs a way to tell us whether it succeeded or failed hence the use of `Result` enum which conveys this message of failure or success When `File::open` succeeds, the value in `greeting_file_result` will be an instance of `Ok` that contains a file handle When it fails the value in `greeting_file_result` will be an intance of `Er` that contains mroe info about the kind o error that occurred