--- title: "Handling Errors in Rust" date: 2022-04-13T15:31:11-04:00 toc: false images: tags: - dev - rust --- > This post is day 12 of me taking part in the > [#100DaysToOffload](https://100daystooffload.com/) challenge. Rust uses a pretty interesting way to deal with errors. Languages like Python and JavaScript allow you to throw errors completely unchecked. C does error handling through the `errno` which is also completely unchecked and not enforced in any way by the programming language. This means any function you call may throw an error, and other than documentation there's nothing to let you know that it might do so. Worse, most of these languages don't tell you what kind of error a function might throw. That's obvious in JavaScript, and TypeScript doesn't help with it either (errors caught have `any` or `unknown` type). C++ and Python let you catch specific types of errors, but you have to rely on documentation to know which types those are. ```typescript try { // ... } catch (err: any) { // Is it a file error? Did I access something undefined? Who knows. ``` Java, on the other hand, requires you to explicitly mark what errors a function can throw and enforces that you handle all the errors or explicitly mark that you are propagating it. ```java public class Main { static void example() throws ArithmeticException; } ``` Rust is a lot closer to this as it enforces that you handle errors. The main difference is that instead of using a special syntax for error handling, it's built into the return type of the function directly. Which is why you'll see functions like this: ```rust fn example() -> Result; ``` This sometimes makes error handling a little harder, but luckily we have many tools that can help us handle errors. ## When you don't care about the error Sometimes you just don't care about the error. Perhaps the error is impossible to recover from and the best you can do is print an error message and exit, or how you handle it is the same regardless of what the error is. ### To just exit Two great options in this case is [die](https://crates.io/crates/die) and [tracing-unwrap](https://crates.io/crates/tracing-unwrap). Both of these options allow you to unwrap a `Result` type, and print a message if it's an `Error` and exit. `die` allows you to pick the error code to exit with, while `tracing-unwrap` uses the [tracing](https://crates.io/crates/tracing) logging framework. You can also always use the built-in [unwrap or expect](https://learning-rust.github.io/docs/e4.unwrap_and_expect.html) functions. ```rust // die let output = example().die_code("some error happened", 12); // tracing-unwrap let output = example().unwrap_or_log() ``` If you are writing a function that might return any type of error, then [anyhow](https://crates.io/crates/anyhow) is your best option. ```rust fn main() -> anyhow::Result<()> { let output = example()?; } ``` ## When you do care about the error If you do care about what type of error you return, then you need [thiserror](https://crates.io/crates/thiserror). `thiserror` allows you to write your own error types and propagate errors. ```rust use thiserror::Error; #[derive(Error, Debug)] pub enum ExampleError { #[error("The error message for this type")] Simple(String), #[error("An error that you are propagating")] FileError(#[from] io::Error), } ```