How to Handle Errors in Rust: A Comprehensive Guide
Tags: rust, programming, beginners
Published: 2022-12-02 13:18:39

Rust community constantly discusses about error handling.. In this article I will try to explain  what is it then why, and how  we should use it.

## Purpose of Error Handling 
Error handling is a process that helps to identify, debug, and resolve errors that occur during the execution of a program. 
It helps to ensure the smooth functioning of the program by preventing errors from occurring and allows the program to continue running in an optimal state. 
Error handling also allows users to be informed of any problems that may arise and take corrective action to prevent the errors from happening again in the future.

## What is a Result? 

Result is a built-in [enum](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html) in the Rust standard library.
It has two variants Ok(T) and Err(E).

 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppuqcc9014437vkvvpdz.jpeg)

Result should be used as a return type for a function that can encounter error situations.
Ok value is return in case of success or an Err value in case of an error.

**Implementation of Result in a function.**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zd4uzu6q9uew1rva0b6s.jpeg)


## What is Error Handling

Sometimes we are using functions that can fail, for example calling an endpoint from an API or searching a file. These type of function can encounter errors (in our case the API is not reachable or the file is not existing).
There are similar scenarios where we are using Error Handling.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lsoo79ebw8ue4j39idgf.jpeg)


**Explained Step by Step**

- A Result<String, io::Error> is the result of the read username from file function.
It follows that the function's returned value will either be an Ok that contains a String or an Err that contains an instance of io::Error.


There is a call to "File::open" inside of read username from file, which returns a Result type. 

- It can return an Ok
- It can return an Err

Then the code calls a match to check the result of the function and return the value inside the ok in the case the function was successful or return the Error value.

In the second function read_to_string, the same principle is applied, but in this case we did not use the keyword return as you can see, and we finally return either an OK or an Err.

So you may ask:  On every result type I have to write all these Match block?

So hopefully there is a shortcut :)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rrburuebo85tf0ggr3ty.png)


## What is the Question Mark- Propagation Error?

According to the [rust lang](https://doc.rust-lang.org/reference/expressions/operator-expr.html) book: 

> The question mark operator (?) unwraps valid values or returns erroneous values, propagating them to the calling function. It is a unary postfix operator that can only be applied to the types Result<T, E> and Option<T>.

**Let's me explain it.**

Question mark (?) in Rust is used to indicate a Result type. It is used to return an error value if the operation cannot be completed. 
For example, in our function that reads a file, it can return a Result<String, io::Error> type, where the question mark indicates that an error might be returned if the file cannot be read, or in the other hand  the final result.
In other words, used to short-circuit a chain of computations and return early if a condition is not met. 

```
fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("username.txt")?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}
```
Every time you see a ?, that’s a possible early  return from the function in case of Error, else , f will hold the file handle the Ok contained and execution of the function continues (similary to unwrap function).


## Why use crates for Handle errors?


Standard library does not provide all solutions for Error Handling..
In fact, different errors may be returned by the same function, making it increasingly difficult to handle them precisely. 
Personal anecdote, in our company we developed [Cherrybomb](https://github.com/blst-security/cherrybomb) an API security tool written in Rust, and we need to re-write a good part of it to have a better errors handling.

For example:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9bh21ityq1szpwwdfvds.jpeg)

Or the same message error can be displayed multiples times.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/of0e8khzy9bwd2lo0ux5.jpeg) 
 

This is why we need to define our own custom Error enum. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9c2fzx3wlgqagtux049.jpeg)


Then our function will look like:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tfmq5gmpjzsxlpqe1k0l.jpeg)





## Customize Errors
 
Thiserror focuses on creating structured errorsand has only  one trait that can be used to define new errors: 

Thiserror is an error-handling library for Rust that provides a powerful yet concise syntax to create custom error types.

In the cargo toml:
`
[dependencies]
thiserror = "1.0"
`

It allows developers to create custom error types and handlers without having to write a lot of boilerplate code. 

Thank to [thiserror](https://crates.io/crates/thiserror) crate, we can customize our error messages.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sen64wpc13dh53nxcboa.jpeg)


It also provides features to automatically convert between custom error types and the standard error type. We will see it in the next Chapter with Dynamic Error.

- Create new errors through #[derive(Error)].
- Enums, structs with named fields, tuple structs, and unit structs are all possible.
- A Display impl is generated for your error if you provide #[error("...")] messages on the struct or each variant of your enum and support string interpolation.

Example taken from [docs.rs](https://docs.rs/thiserror/latest/thiserror/):

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9x6oj5pwpaww9s0sgzqb.png)

## Dealing Dynamic Errors handling   
 
If you want to be able to use?, your Error type must implement the From trait for the error types of your dependencies. Your program or library may use many dependencies, each of which has its own error you have two different structs of custom error, and we call a function that return one specific type.
For example:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/myivur7e6jajhbb2p94g.jpeg)


So when we call our main function that return a `ErrorA` type, we encounter the following error:
 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m7mvg1yefr8r78sv0ptk.png)


So one of the solution is to implement the trait `From<ErrorB>` for the struct `ErrorA`.


Our code looks like this now: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7fwpocv129fd3ajkrus4.jpeg)

 Another solution to this problem is to return dynamic errors.
To handle dynamic errors in Rust, in the case of an Err value, you can use the box operator to return the error as a Box<dyn Error> (a trait object of the Error trait). This allows the error type to be determined at runtime, rather than at compile time,  making it easier to work with errors of different types. 
 
  
The Box can then be used to store any type of Error, including those from external libraries or custom errors. The Box can then be used to propagate the Error up the call stack, allowing for appropriate handling of the error at each stage.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/18b2kenm8eef2q54upah.jpeg)

## Thiserror crate

In order to have a code clearer and soft let's use thiserror crate.
The `thiserror` crate can help handle dynamic errors in Rust by allowing the user to define custom error types. It does this through the `#[derive(thiserror::Error)]` macro. This macro allows the user to define a custom error type with a specific set of parameters, such as an error code, a message, and the source of the error. The user can then use this error type to return an appropriate error value in the event of a dynamic error. Additionally, the `thiserror` crate also provides several helpful methods, such as `display_chain`, which can be used to chain together multiple errors into a single error chain.
In the following  we have created our error type `ErrorB` , then  we used the From trait to convert from `ErrorB` errors into our custom  `ErrorA` error type. If a dynamic error occurs, you can create a new instance of your error type and return it to the caller. See function `returns_error_a()` in line 13.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uo4vzlccwdev55itija0.jpeg)

## Anyhow crate

anyhow was written by the same author, dtolnay, and released in the same week as thiserror.
The [anyhow](https://crates.io/crates/anyhow) can be used to return errors of any type that implement the `std::error::Error` trait and will display a nicely formatted error message if the program crashes.
The most common way to use the crate is to wrap your code in a Result type. This type is an alias for the `std::result::Result<T, E>`  type, and it allows you to handle success or failure cases separately. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mvegdox4f58x27sf0dta.jpeg)

When an error occurs,for example you can use the `context()` method to provide more information about the error, or use the with_chain() method to chain multiple errors together. 
 The anyhow crate provides several convenient macros to simplify the process of constructing and handling errors. These macros include the `bail!()` and `try_with_context!() `macros.
 The former can be used to quickly construct an error value, while the latter can be used to wrap a function call and automatically handle any errors that occur.



##  Comparison 


The main difference between anyhow and the Thiserror crate in Rust is the way in which errors are handled. Anyhow allows for error handling using any type that implements the Error trait, whereas Thiserror requires you to explicitly define the error types using macros. 

Anyhow is an error-handling library for Rust that provides an easy way to convert errors into a uniform type. It allows  to write concise and powerful error-handling code by automatically converting many different types of errors into a single, common type.


In conclusion,in [Cherrybomb](https://github.com/blst-security/cherrybomb) we choose to  combining the two, in order to  create a custom error type with thiserror and  managed it by the anyhow crate.