Rust: Error handling
Tags: rust, programming, beginners, tutorial
Published: 2020-05-03 18:28:53

This blog covers error handling in Rust with simple examples. 

Rust does not have exceptions - it uses the `Result` enum which is a part of the Rust standard library and it is used to represent either success `(Ok)` or failure `(Err)`.

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

When you are writing functions and need to handle potential problems, you can exit the program using the [`panic!` (macro)](https://doc.rust-lang.org/std/macro.panic.html) - this should be rare

### `panic`

```
fn gen1() -> i32 {
    let mut rng = rand::thread_rng();
    let num = rng.gen_range(1, 10);
    if num <= 5 {
        num
    } else {
        panic!("{} is more than 5", num);
    }
}
```

> Here we call `panic!` if the random number is more than 5

To make our intentions clearer, we can return a `Result`

### Using `Result`

```
fn gen2() -> Result<i32, String> {
    let mut rng = rand::thread_rng();
    let num = rng.gen_range(0, 10);
    if num <= 5 {
        Ok(num)
    } else {
        Err(String::from(num.to_string() + " is more than 5"))
    }
}
```

In this case, if the number is less than equal to 5, we return the `Ok` variant else, we return an `Err` with a `String` message

> we could have used an [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html) return type in this case, but in this case, we are using result since `a number more than 5` is an erroneous scenario (in our fictitious use case)

Now we have a method which returns a `Result`, let's explore how a caller would use this method. There are a few options:

- Use `panic!`
- Acknowledge the problem and handle it
- Pass it on

### `panic` (again!)

This is the easiest but most often, the least desirable way to handle problems. Just panicking is a terrible idea. We can make it a little better, if not ideal

**use `expect`**

[`expect`](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect) is a shortcut - it is a method available on `Result` which returns the content from the `Ok` variant if its available. Otherwise, it `panic`s with the passed in message and the `Err` content

```
fn caller() -> String {
    let n = gen2().expect("generate failed!");
    n.to_string()
}
```

**use `unwrap`**

`unwrap` is similar to `expect` apart from the fact that it does not allow you to specify a custom message (to be displayed on `panic!`)

```
fn caller() -> String {
    let n = gen2().unwrap();
    n.to_string()
}
```

We can do a little better with `unwrap_or()` and use it to return a default value in case of an `Err`

```
fn caller() -> String {
    let zero = gen2().unwrap_or(0);
    zero.to_string()
}
```

> In this case, we return `"0"` (String form) in case of an error


### Handle the problem

```
fn caller() -> String {
    let r = gen2();
    match r {
        Ok(i) => i.to_string(),
        Err(e) => e,
    }
}
```

In this case, we `match` on the `Result`: return a `String` (using `i.to_string()`) or return the `Err` content (which also happens to be a `String` in this case)

### Not my problem.. pass it on

What if you did not want to handle the problem and need to propagate it to the caller (in a slightly different way)?

Instead of returning an explicit `String`, you can return a `Result<String,String>` where `Ok` variant will contain a `String` result and an `Err` variant will (also) contain a String result explaining with error details

> note that a `String` has been used to represent error (`Err`) as well. This is just to keep things simple. Typically, you would use a special error type e.g. `std::io:Error` etc.

Here is one way:

```
fn caller() -> Result<String, String> {
    let r = gen2();
    match r {
        Ok(i) => Ok(i.to_string()),
        Err(e) => Err(e),
    }
}
```

We change the return type to `Result<String, String>`, invoke the function and `match` (which returns a `Result<i32,String>`). All we need is to make sure we return the `String` conversion of `i32`. So we match against the returned `Result` - if its `Ok`, we return another `Ok` which contains the `String` version of i32 i.e. `Ok(i) => Ok(i.to_string())`. In case of an error, we simply return it as is

### `?` operator

We can simplify this further by using the `?` operator. This is similar to the `match` process wherein, it returns the value in the `Ok` variant of the `Result` or exits with by returning `Err` itself. This is how its usage would look like in our case:

```
fn caller() -> Result<String, String> {
    let n = gen2()?;
    Ok(n.to_string())
}
```

The method is called - notice the `?` in the end. All it does it return the value in `Ok` variant and we return its `String` version using `Ok(n.to_string())`. This takes care of the one half (the happy path with Ok) of the `Result` - what about the other half (Err)? Like I mentioned before, with `?` its all taken care of behind the scenes, and the `Err` with its value is automatically returned from the function!

> Please note that the `?` operator can be used if you a returns `Result` or `Option` or another type that implements `std::ops::Try`

**What if the `Err` type was different?**
You can/will have a situation where you're required to return a different error type in the `Result`. In such a case, you will need to implement `std::convert::From` for your error type. e.g. this won't work since `Result<String, NewErr>` is the return type

```
...
//new error type
struct NewErr {
    ErrMsg: String,
}
...
fn caller5() -> Result<String, NewErr> {
    let n = gen2()?;
    Ok(n.to_string())
}
```

You need to tell Rust how to convert from `String` to `NewErr` in this case since the return type for the `gen2()` function is `Result<String,String>`. This can be done as such:

```
impl std::convert::From<String> for NewErr {
    fn from(s: String) -> Self {
        NewErr { ErrMsg: s }
    }
}
```