Rust Is Really Cool
- Immutability by default
- Pattern matching
- Error handling done right
- Null references: The billion dollar mistake
- Rust compiler teaches you
- Conclusion
- Other resources
As a web developer, mainly using TypeScript, PHP, etc, I wanted to explore a completely different language, stepping out of my comfort zone and exploring new ways of doing things.
Rust’s selling point for a large group of people is its performance, but I was particularly interested in its error handling and functional language features.
I am far from being an expert in Rust, but I would like to share the topics that have caught my interest the most, changing my perspective on software development.
Immutability by default
In Rust, variables are immutable by default. to declare a variable as mutable,
you must use the mut
keyword. Functions also need to declare if they intend to mutate their arguments.
We must be explicit about whether we are going to change the value of any variable, making our code more robust, predictable, and easier to reason about.
Pattern matching
Rust’s pattern matching syntax is a powerful tool for handling complex data types and writing robust and expressive code.
The match
statement allow us to compare a value against a series of patterns
and then execute code based on which pattern matches. We can represent all the possible states of our application and safely handle every scenario in a concise and expressive way.
Pattern matching is exhaustive, which means we must handle all possible cases, eliminating the risk of missing critical scenarios and potential bugs. Otherwise, the program would not compile.
Error handling done right
Rust treats errors as values. instead of throwing and error and hoping that some function above will catch it, if your code can produce an error, the compiler requires you to handle it or explicitly pass it to the next function.
Result
is an enum
which value can be either Ok
or Err
. each variant represents the success or failure of the operation, with its resulting value.
Consider this code that reads from a text file. The read_to_string
function
has a return type of Result
. that indicates that the code might contain an error, thus forcing to handle it before using the result.
If we do not want to handle it now, we must modify the function signature to indicate that the result of this function could potentially be an error.
By combining errors as values with enums
and pattern matching, Rust ensures that we correctly handle all possible errors in our code.
Comparing it to TypeScript, for example, we can see the strength and correctness of the former solution.
In TypeScript, errors are thrown without any built-in mechanism to ensure that all error scenarios are properly handled or even if they will be handled at all.
There are a few libraries that try to solve this exact problem in TypeScript, but they are not part of the language itself, and they have performance implications, limitations and other caveats.
Null references: The billion dollar mistake
The problem with null values is that if you try to use a null value as a not-null value, you will get an error of some kind. because this null or not-null property is pervasive, it’s extremely easy to make this kind of error.
In his 2009 presentation “null references: the billion dollar mistake,” Tony
Hoare, the inventor of null
, has this to say:
I call it my billion-dollar mistake. at that time, I was designing the first comprehensive type system for references in an object-oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. - Tony Hoare
The problem isn’t really with the concept of null values, but with the
particular implementation. As such, Rust does not have nulls, but it does have an enum
that can encode the concept of a value being present or absent.
Option
is an enum
that encodes the very common scenario in which a value
could be something or it could be nothing. An Option
type can be either Some
, with a value, or None
, that represents the absence of a value.
The Rust compiler ensures that you convert an Option<T>
to a T
before you
can perform T
operations with it. Generally, this helps catch one of the most common issues with null: assuming that something isn’t null when it actually is.
Rust compiler teaches you
Learning Rust can be quite challenging. understanding concepts such as the ownership model, string types, generics, traits, lifetimes, macros, and complying with the strict and authoritative rules of the language, can be overwhelming. However, one thing that made my learning journey easier was to learn from the compiler errors.
Initially, you may make several fundamental mistakes because you do not yet understand how Rust works. The compiler is very strict, but it is also extremely useful, showing precisely where the error occurred, the probable solution to it, and a brief explanation along with the corresponding error code.
Conclusion
Rust is unfamiliar, and can be tough to learn, but it’s worth the effort. it is a strict language with a steep learning curve; however, this design choice helps developers understand its complexity early on and avoid bugs in the future. Keep this in mind before starting to learn Rust.
Rust is a robust, elegant, and powerful language. Although I will probably not use it at work, I have found it a very enjoyable language to learn, despite being quite challenging. It has changed my view on software development and how things can be done, and most importantly, it has been really fun.🦀
Other resources
Most of the code examples are taken from the Rust book, and other cool resources.