First impressions about Rust

I finished my first patch with Rust! \0/ I don’t feel specially proud of it but I’m happy because it is my first contribution to a real project. It has a lot of flaws and technical debt but this is only a prototype and a sandbox for Rust exploration.

In short, the experience is exciting followed by a deep sense of uncertainty, then frustration with a small pinch of enlightenment just to be followed by a rewarding sensation at the end.

You can see 4 cycles here, each with stages of excitement, uncertainty, frustration, enlightenment and finally, the step upwards depicts the reward.

It is exciting because it introduces a lot of new syntax (for me, a new, cool and different syntax compared with JavaScript or Python) with lots of annotations and symbols everywhere. I feel like engineering something precious, delicate but at the same time I feel confident on its robustness cause I’m adding tons of metadata for the compiler. Furthermore, I’ve followed some tutorials and the compiler errors seems very clear, they are even helping and advising me. I’ve read about borrowing, lifetimes and mutability but in the tutorials those never seemed to be a problem.

Then it comes uncertainty. I know what to do but I don’t know how to write it. When learning a new language I lack from two things: a deep knowledge of the API and idioms. And idioms are the most important feature of a language because in the long term you prefer to read idioms. These are patterns that immediate associate with well know and repetitive behaviors. For instance. These three snippets do the same:

// Option 1
if let Some(value) = maybe {
} else {
  Err("Some error")

// Option 2
if maybe.is_none() {
  return Err("Some error");

// Option 3
maybe.ok_or_err("Some error")

My first approach was option 2. It is very similar to C or JavaScript. But then I remember the people that writes Python as it was C. I hate them. Probably I hate you. Naaaah… just joking. But as a Python and JavaScript teacher I try to explain why idioms matter. Then I turned to option 1 and only for this post and after investigating the Option API, I realize about option 3. Currently, I don’t know what is more idiomatic but I would bet for the last option.

Once you are happy with your third refactor, you finally launch the tests and they fail… No, wait! That happens, but much later. First it comes the compiler, the type reasoner and the borrow checker. Let me illustrate the problem with an example. Suppose you want to test the former option 2: in play Rust… (no! no the f*ck*ng game, I did not play it yet but I already hate it), this play Rust(-lang). You want to enclose it in a function, then call it. No run, just see if it compiles.

My first try failed with:

<anon>:1:16: 1:22 error: wrong number of type arguments: expected 1, found 0 [E0243]
<anon>:1 fn test(maybe: Option) -> Result {
<anon>:1:16: 1:22 help: see the detailed explanation for E0243
<anon>:1:27: 1:33 error: wrong number of type arguments: expected 2, found 0 [E0243]
<anon>:1 fn test(maybe: Option) -> Result {
<anon>:1:27: 1:33 help: see the detailed explanation for E0243
error: aborting due to 2 previous errors
playpen: application terminated with error code 101

Read the errors. Try again… Seriously, read the errors. You’ll discover that they make a lot of sense, Option must be an Option of some type so be it Result (but Result needs two types, one for valid values and other for errors).

The second try failed with:

<anon>:3:20: 3:32 error: mismatched types:
 expected `collections::string::String`,
    found `&'static str`
(expected struct `collections::string::String`,
    found &-ptr) [E0308]
<anon>:3         return Err("Some error");
<anon>:3:20: 3:32 help: see the detailed explanation for E0308
error: aborting due to previous error
playpen: application terminated with error code 101

Well, now it turns out string literals in Rust are not String but &’static str. What does it mean? That means reading the docs again. Fortunately the compiler gives you enough info to solve the errors. Uncertainty again, I could convert this &str to a real String (expected in the errors) but I’m happy with a Result of type &str (found in the errors). So, I modify the signature of the function and…

Bam! My third try failed again with:

<anon>:1:43: 1:50 error: missing lifetime specifier [E0106]
<anon>:1 fn test<T>(maybe: Option<T>) -> Result<T, &String> {
<anon>:1:43: 1:50 help: see the detailed explanation for E0106
<anon>:1:43: 1:50 help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
<anon>:1:43: 1:50 help: consider giving it an explicit bounded or 'static lifetime
error: aborting due to previous error
playpen: application terminated with error code 101

What the hell!? Ok, now I need a lifetime specifier. Lifetime specifier…, lifetime specifier… where are those bl**d* specifiers!? Oh, here! Well, you get the idea. This time the problem was I missed a lifetime specifier. As Rust needs to know how much this reference is going to live to check if the returning value lives long enough to not leave you with dangling pointers. Do you understand? Well, probably not, but I do (or at least, I did) which leads me to the next point: enlightenment!

The fourth try worked seamless.

Trying to figure out which kind of lifetime I want to assign for the returning structure, I though: I want these errors to be immortal strings (which translates into ‘static lifetimes) in order to not produce new strings and reuse them. Suddenly, for a fraction of time, I understand everything: lifetimes, specifiers, elision and immortality (well… staticness) and then… it’s gone. This is what I call enlightenment. No more than a fraction of second you understand what are you doing in this new context which is Rust.

Earth seen from the International Space Station
Similar to what astronauts call overview effect, this is what you experiment with Rust from time to time. When you are not fearing about the huge fall you have below.

Paradoxically, If I would read the errors from the second try more carefully, I should have read that the expected type was &’static str preventing me from the third try but at the same time impeding this moment of truth to happen.

The remaining sensation is rewarding. Bit after bit, I’ll adapt my mind to these new memory and execution models. Reading articles, answers and books I will learn more knowledge about the internals of the data and execution models for Rust at the same time I will borrow some idioms.

Of course, once the code compiles, test are failing and the cycle repeats again. Most hard part is frustration and uncertainty (what else?) but it is completely normal. You can think of yourself as some kind of ninja or choosen-one inside your comfort zone. Modesty aside, I think this of myself when working on JavaScript or Python but when you are in the wild, failure is something normal. During these days, I even realized that my way of debugging / developing with dynamic languages is not suitable for this strongly typed language! But if it was easy, where would be the fun?

Look at those bullet holes on Neo shirt: failure is natural before enlightenment. Mr Anderson learned it the hard way.