Errors and Exceptions (in Rust)
Developer Careers Giovanni Capuano
An introduction to errors and exceptions handling using Rust as example.
Error: An unusual and unexpected situation in the running program that can be resolved only by fixing the program (i.e. out of memory).
Exception: An expected and irregular situation that happens at runtime (i.e. read-protected file).
The main difference here is that errors are unexpected (and usually lead to exploitable bugs) and are often hard to predict (did someone whisper overflow?), while exceptions are expected (you know that they could happen) and usually lead to a crash of the application when not handled (for example when you want to modify a file that actually does not exist but you didn’t think about this possibility).
How popular languages deal with exceptions
We could split programming languages into four categories based on the way they handle exceptions:
- Strict exception handling: the language provides full and explicit coverage to exceptions. Exceptions are defined as classes (either built-in and defined by the developer) that represent the type of exception they handle. Methods are shipped with a list of exceptions they can rise (explicitely or just by invoking other functions) and require to be handled with a
Try...catch-like construct. Not handled exceptions lead to a compile-time error.
- Weak exception handling: everything that’s been said for the strict exception handling languages with the addition that developers are not required to handle exceptions and methods are not shipped with any definition of the exceptions they can rise demanding this task to the documentation. Exceptions that are not handled lead to an abortion of the application at run time.
- Exceptions-agnostic: exception handling is not provided by the language but can be implemented (usually with conditional statements and
- By monadic types: exception handling is realized with generic structs in which values are encapsulated.
Usually handling exceptions in this very OOPish way is quite expensive in terms of resources. The C programming language does not have any knowledge about exceptions and most projects (the Linux kernel included) handle them with
gotos to jump to the block where a task gets rescued. It’s obvious that this trait asks developers to be completely aware about all the edge cases of their code and to write especially clean code.
The last category is the most appreciated inside the functional programming world. The concept is that instead of returning
NULLs or raising exceptions in case of failures (i.e. an hashtable that does not include a given key), an option type is used that can either encapsulates a value or being empty. Haskell uses an option type called
Maybe that accepts
Just x while Rust, Scala, OCaml, Swift and others use an
Option type that accepts
Some(T). Through decostruction via pattern matching, the developer can handle both of these cases.
Let’s see shortly what in practice this does mean exemplifying with a language that despite is not purely functional, implements and makes extensive use of monadic types.
Exception handling in Rust
As said, talking about exceptions, the only tools Rust gives to developers are
Result<T, E>. They are everything but magic, being so easy that you can implement them by yourself in a couple of lines, as done in the article linked above. And they are terribly optimized in compile-time too, so that their usage is almost cost-free.
I’m the kind of person who prefers to leave the code to talk for itself. Please have a look at this snippet.
We define a dictionary containing the name of the books as key and the count of volumes in the stock as value. The
get() method returns an option type that we want to deconstruct through pattern matching, extracting so its value if it’s present.
Instead of using pattern matching we can also
unwrap() the value directly in this way. This method basically extracts the value from an
Option<T>, assuming it
It is quite a double edged weapon though. If the
is_none(), meaning that it has not a value encapsulated, it will
panic! as you can see here.
panic! is a macro defined in the Rust’s standard library that aborts the current thread (this means that our multi-threaded applications can be still alive too :)), causing most of the times the crash of the application, in the same flavor of a Ruby or Python’s unhandled exception.
We don’t care that our returned value is an
Option<T> instead of the plain value as long as we don’t need to use it. Pattern matching,
unwrap_or_else() (documented here) are usually everything we need to keep our code safe from unexpected values that can be returned.
We can live in a world without
NULLs. It sounds kinda like a dream, doesn’t it? But it’s real.
This thing really looks like an exception class. If option types represent a possibility for a value to exists, result types represent a success or a failure of a task (i.e. the creation of a file).
Basically it works like
Option<T>, but instead of accepting
None, these options are replaced by
Ok(T) basically works exactly like
Some(T), encapsulating a generic value, while
Err(E) is a wrapper for a value that represents the error (as it is generic, despite what it can looks like, it can be whatever thing you like, a string containing an error message included).
What about errors?
Initially we said that errors are unexpected and unusual situations in which the program can end into.
Most of the languages represents the most common errors with actual exceptions (i.e.
OutOfMemoryError) but they’re not really supposed to be handled, and so what they do is only giving a reasonable message about the reason of the crash of the application.
Smart compilers like Rust’s help avoiding errors in compile time, providing strict rules and checkings on everything the programmer does, like the ones related to the memory management.
In case of errors like an index out of an array’s bound (which index could be inputted by the user and cannot always be expected, for example), actually there’s a brand new
panic::catch_unwind() method that does exactly what
Begin...rescue do, but it needs to be used conscientiously, as it is not a shorthand for option and result types. Please read Rust 1.9 announcement to learn more about catching
panic!s and more.
Join Honeypot today and receive 4 interview invites or more in three weeks.
Searching for a Tech Job? Join Honeypot!
Let companies in Germany and the Netherlands Apply to You!