DEV Community

Cover image for 30 Days of Rust - Day 24
johnnylarner
johnnylarner

Posted on

30 Days of Rust - Day 24

Good evening everyone, I'm back after a few days off. I had a friend from abroad visiting and put my studies to one side. I'm back now and have three Rust blogs planned for this week. Today's blog will be short and touch on error handling concepts rather than novel features

Yesterday's questions answered

No questions to answer

Today's open questions

No open questions

Handling errors like a pro

We've already looked at how errors are handled in Rust. But today while building a simple CLI tool (a chapter in the Rust book), I learned some new techniques for error handling could be applied to Rust an beyond.

Keep things simple

When reading code you have a number of markers of cognitive complexity (in terms of syntax, not the concept being represented in code):

  1. Vertical complexity: how deep am I in the stack when this code runs?
  2. Horizontal complexity: how many related data points do I need to track to understand how a function works?
  3. Durational complexity: how long do I need to follow the code before I can "drop" variables ?

When it comes to error handling, we often ask ourselves: where do I raise an error? This indicates to me that reducing vertical and durational complexity can help us handle errors better. In practice, this means two things:

  1. Errors should be handled at a single, higher level. Errors represent potential exit points. Therefore grouping these exit points at one point in call stack can make your code more expressive.
  2. Handle errors as early as possible. If we can rule out all errors early on, then whatever remaining code can be simplified as the "happy path".

Nuanced error handling syntax

Rust executables often contain a run function. This contains the application logic and often serves the purpose of bubbling errors up to your main file, as well as making your code testable. As a run function typically returns an empty tuple, often don't want to do anything with the variable.

The idiomatic of handling such errors in Rust is to use an if let statement:

if let Err(e) = minigrep::run(config) {
    eprintln!("Application error: {e}");
    process::exit(1);
};
Enter fullscreen mode Exit fullscreen mode

We don't care about the empty tuple returned by the run function, so we can save some noise in the catch all variable.

However, it's more often the case that we do, indeed, care about what our function returns to use. In that instance, we'd want to use the unwrap_or_else method of the Result type:

let config = Config::build(&args).unwrap_or_else(|err| {
    eprintln!("Problem parsing arguments: {err}");
    process::exit(1)
});
Enter fullscreen mode Exit fullscreen mode

Top comments (0)