Rust — Control your Errors by owning it

0xksure
5 min readMay 15, 2021

--

Photo by Sigmund on Unsplash

In my previous post we started looking at how to get started with Rust or more specifically how to set up a web server in Rust using the rocket framework.

Here is the github repo if you want to check out the code for yourself

The end result became a web server that returned a random number using the rand crate when polled. It was meant to illustrate that Rust is well suited for web server development and pretty easy to get started with.

Now as the number of endpoints increases it is often beneficial to split the code that makes up an endpoint into smaller components. This not only improves usability but also the readability of the code. Now, one of these patterns that I myself is a fan of is to split the endpoint code into handlers and models. For which the handlers takes care of the incoming requests and the models deals with the business logic. It is also useful to have common code in a separate folder. This is often the case for sql queries.

In Rust it’s best practise to return the Result type enum

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

for which T and E are generic parameters (Yay, Rust got generics, how about that go). The former is returned when everything goes as expected while the latter is returned when something unexpected happens i.e. an error occurs. Usually one is pretty explicit in what value T should be but maybe not so much E. So the question is which Error type should you use?

Standard Error Type

In the beginning when you start with Rust, or any language, it is natural that you want to get up and running with the project of your dreams as fast as possible. This often leads to cutting some corners. When I started out I was exposed early to the io::Error in the standard library

use std::io::{Error,ErrorKind}let new_error = Error::new(ErrorKind::Other,"this is an error);

and so I used that a lot just to get by learning Rust. This is of course not optimal.

There is probably some crates out there that solves this problem pretty neatly but then again aren’t we here to learn Rust.

So now that we have started creating a web server it seems kind of suboptimal to use an io error type when it would make much more sense to use a custom say web error type. So this is what we are going to do now! 🎉

Custom Error Type

Finally, you made it through the “motivational” part and are ready to roll up your sleeve and start swiftly hitting you keyboard. Since we are only going to use ourErrortype in our main.rs creating a module will do. So let’s get started with our module.

A basic web error module can look something like this

On line 1 we make our module web_error a public module so that it is accessible from our main.rs file. Then we go ahead and define the Error struct on line 2 which will have a status and a message field. As you notice they are both of type String. Of course status should probably be a discreet set of values, typically an enum. This can be fixed later on. Continue on line 7 we make our Error implementation to have a new method (line 8) that takes in the status and message and return the Error type.

Right now our web error doesn’t do much. Let’s iterate on our previous random number generator endpoint to allow for a random number range between(min,max).

Now there are a few changes since last time we crated the rocket web server. There is a lot of new things going on. First of all notice that we import the weberror module on line 7 and the corresponding Error type on line 8.

Instead of generating the random number inside the random_number function (handler, remember the preface? ) on line 19 we have now moved the business logic to its own function generate_a_random_number. Pretty self explanatory right? The function takes the random number range in form of two parameters, min and max. These are 32bit floating point. The function then generates a number between [0,1) and scales it to be in the [min,max] range. Then we return the random number as a 32bit integer type. Notice that on line 12 we return an error if max < min . This is where the Result enum return type comes in handy.

Finally, we call the generate_a_random_number on line 20 using the match arms. Looks great! Or oh no we get an error on line 22! It says

--> src/main.rs:22:41
|
22 | Err(err) => return format!("{}",err)
| ^^^ `web_error::Error` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `web_error::Error`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required by `std::fmt::Display::fmt`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

Wow that’s a lot of information! So the obvious error here is the first one, namely

help: the trait `std::fmt::Display` is not implemented for `web_error::Error

So our web_error::Error does not implement Display i.e. Rust simply doesn’t know how to format the error. Since we have become familiar with the Rust docs we can head over to https://doc.rust-lang.org/std/fmt/trait.Display.html. Here we can find an example of how to implement the fmt::Dispay trait for, in this example, the struct Point:

impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}

That looks pretty straight forward. So the result become

So similar to what was done in the documentation we implement fmt::Display for Error on line 18–25. Finally we can run cargo run in the root of the project (where the Cargo.toml lives) and the rocket web server (should) be up and running.

To test it out visit localhost:8000/random in your favorite browser or make a GET request. You should get a random number every time you poll the endpoint.

To test the error handling we can jump back into the main.rs file and set min greater than max like this

Now the change happened at line 20 where min=22.0 and max=20.0 . Restart the server and try polling the endpoint again. In return you should get something like this

{
status: InternalServerError,
message: max has to be greater than min,
}

Woop! 🎉 We have finally our own custom web error module in Rust.

Obviously there are some slight modifications that can be made to improve the web error, like

  • Use an external status enum that represents all possible server statuses, even I’m a teapot (418)
  • Return a json object instead of a string
  • Make the Error a crate instead of a module.

Conclusion

Having your own custom error type can be really helpful when your project grows and you want to display your errors in a specific way. Throughout this article we have looked at how we can create a custom error module that we can use specifically in our Result enum type in the case of unexpected errors.

Feel free to checkout the branch containing all the code for this post on

And don’t forget to follow me on twitter for more rust related

happy hacking!

--

--