Thoughts on Rust after six months
anelson February 22, 2019 #tech #rustDisclaimer
Iām writing this post as an honest and frank description of my experience coding in Rust these last six months. Itās just, like, my opinion, man. Maybe you would have a completely different experience. Maybe Iām doing it all wrong and should be regarded with a blend of pity and contempt. Maybe Iām a Russian troll sowing discord as part of some inscrutable geopolitical long game. I leave it to you to decide for yourself.
Background
First off I should admit this is not my first time with Rust. I tried Rust back in August 2015, and I did so in a public repo so you can see for yourself the ensuing debacle. My final commit message in that repo was thus:
Came to the conclusion that Rustās ownership model is too restrictive to be enjoyable to use (see src/wtf.rs)
I donāt remember what the specific problem was, but I know Rust was a different language back then, and I was certainly a different programmer. I can see from the code I wrote then that I was not making enough effort to understand the mechanics of the borrow checker, and thus I wrote non-idiomatic Rust code and fought constantly against the compiler. I know many other programmers have had this same experience, and I think anyone who approaches Rust expecting to write C++ or Java-like code without understanding Rust idioms is bound to have a bad time.
The tl;dr is: Rust is a very poor language for anyone who wants to write (C++/Java/etc) code. If you want to program in Rust, you need to write Rust code. Tautology? Youād be surprisedā¦
Present attempt
For the last six months or so Iāve been working on a few Rust projects. None of them are in public repos yet, but they will all eventually be open sourced under the dual MIT/Apache 2.0 license favored by the Rust community. The details of my projects arenāt particularly important, but only to say that they are non-trivial libraries in which high (and consistent) performance and concurrency are critical. Given Rustās reputation it should be a perfect fit.
How not to learn Rust
I alluded above to the fact that if one wants to learn Rust, one must be open to a very different way of approaching some problems. I can virtually guarantee that anyone with prior programming experience in any modern statically typed language will initially find themselves fighting the Rust compiler for every inch of ground. It will be frustrating, and not at all productive.
I think noted Rust programmer and author George Bernard Shaw said it best:
I learned long ago, never to wrestle with the Rust compiler. You get dirty, and besides, the compiler likes it.
- George Bernard Shaw
How to learn Rust
My advice to anyone who is genuinely curious about Rust and genuinely interested in trying it out, is to approach it with humility and an open mind. First read the book, and then try to write something of your own. If youāre anything like me, youāll fail in a cascade of messages about missing lifetimes and types without a size and traits not being objects. It can be frustrating at times, but you should try to cultivate the right mindset: many smart and experienced programmers enjoy using Rust every day, they canāt all be masochistic imbeciles, maybe thereās something Iām missing, and it will be interesting to figure out what it is.
I should also point out that the Rust community on Stack Overflow is very active and very helpful. If you get stuck they can help, though most likely someone else already asked the exact same question and the help is only a search away.
My only complaint about the available resources for learning Rust is that they are very poorly SEOād. I very often get
results from older editions of The Book, from mailing lists and RFCs that are ancient and no longer reflect modern Rust,
or even worse RFCs or GitHub issues discussing some cool new future feature of Rust that isnāt ready yet and may never
land. Though Iām an avid user of DDG, I found myself having to prefix all my Rust searches
with !g
because Google seemed to do a much better job of pulling relevant results.
How to fight the compiler
The only way to get smarter is by playing a smarter opponent. The Rust compiler is a smarter opponent.
- Fundamentals of
ChessRust Programming 1883
Rust reminds me a lot of Scala in that the compiler is fanatically pedantic, seemingly incapable of the slightest bit of imagination. If anything Rust is worse in this regard, due to the incorporation of lifetimes into the type system. Over the last six months Iāve come to expect a mighty struggle for every successful build, and in fact Iāve come to relish it.
I canāt emphasize enough the importance of mindset, particularly early on. The Rust compiler is an incredible achievement, not only in the use of borrow checking for provably-correct elimination of an entire category of bugs, but even more so for the astonishing helpfulness of most of its error messages. While itās sometimes frustrating trying to understand how to fix some of the errors it reports, Iām consistently amazed by the humane quality of the compiler output. Take advantage of this achievement and learn from the compiler. As I gained experience with its particular brand of pedantry, I found myself anticipating its objections, and structuring my code differently. Not just differently; better. More idiomatically Rust, yes, but also, subjectively, better code.
I heard this time and again from other programmers new to Rust: the Rust compiler forced me to become a better programmer. This was my experience as well. Iāve lost count of how many times Iāve written code that was obviously correct, only to have it soundly rejected by the compiler. In the ensuing fusillade of profanity in which the virtue of the compilerās mother is rudely impugned, Iād then come to the humble realization that itās correct, and that what I was trying to do has some subtle edge case or failure mode I didnāt see. After Iād meekly change the code or add an explicit lifetime or change the scope or whatever I need to do, Iād be forced to admit the result is simply better code.
rustfmt
is fantastic
For most of my programming career, Iāve been fanatical fairly opinionated about code style. I long wished there
were some tool that could be run on (other peopleās) code so that it would look right like my code. I tried a few
tools over the years but I always got bogged down in litigating specific details of this style or that style and never
actually got anything done.
A few years ago, while coming to the conclusion that Go is a language that simply rubs me the wrong way, I had occassion
to use gofmt
. Easily the best feature of the Go ecosystem. There are no configuration options, no ability for your
team to bikeshed or litigate on which indentation style is best. Thereās only one way, and itās the gofmt
way. That
imperious arrogance is part of why I really, really came to dislike Go, but I wonāt hesitate to give praise where itās
warranted: gofmt
is the right answer.
Thankfully thereās rustfmt
for Rust and itās similarly great. Though it has more configurability than gofmt
, it
seems to be generally accepted within the Rust community that the defaults are the right answer. This is even built in
to the official Rust plugin for Vim, so rustfmt
formatting can be automatically applied on save. Because rustfmt
must first run the code through a much less thorough and complete version of a compilation cycle, Iāve noticed that even
without looking at the compiler output I can tell when thereās an error somewhere if on save my code isnāt slightly
reformatted by rustfmt
.
Seriously, be careful with rustfmt
. If you use it for a little while and then have to go back to a language without
a universally accepted beautifier, you are likely to seriously regret some of your life choicesā¦
clippy
is great too
clippy
is a linter for Rust. While the rustc
compiler compiles the code, and rustfmt
handles formatting, clippy
analyzes working, compiled code for all manner of possible improvements. Itās not on be default, but I strongly suggest
you install it and configure it to fail the build on any violations of the checks that are turned on by default.
I havenāt experimented with turning on the more pedantic checks, because Iām sure theyāre called āpedanticā for
a reason.
The great thing about clippy
for a beginner is it finds and points out all sorts of small mistakes that you otherwise
might not notice. For example, maybe you are using unwrap_or
when unwrap_or_else
would be sufficient, or you tried
to Box
a Vec
which is just a wasted allocation. As hard as it is to write Rust code that will actually compile,
thereās still plenty of room for mistakes that get past the compiler only to be caught by clippy
.
cargo
and rustup
make dependencies effortless
Though I donāt intend to write a post bashing Go, I canāt help but take another swipe here. Goās dependency management story was absurdly awful when I tried it (in fairness that was years ago maybe it sucks less now). So bad that the highest praise I could think of at the time was āitās better than C++ā. Ouch.
I mention this only by way of contrast to Rustās dependency story. It is honestly not much different in ergonomics than
npm
for Javascript or bundler
for Ruby. Well, except that Rust is a strongly-typed, compiled, low-overhead systems
language! Itās such a joy to find the crate you want, add it to Cargo.toml
, then cargo just does what it needs to do.
āWhat it needs to doā usually means resolving a long dependency graph, downloading the code for all the new transitive
dependencies, compiling them, and resolving your use
statements to the right libraries. Honestly itās still
impressive to me how well cargo
works. I have very quickly come to take it for granted.
Coming as I did most recently from Scala, I can compare cargo
most directly to sbt
. There is no comparison. sbt
is a radioactive dumpster fire of pathological over-engineering (does the s
still stand for āsimpleā??), and the
Maven/Ivy dependency scheme isā¦well, itās exactly what one expects of the Java ecosystem, I canāt think of harsher
criticism than that. cargo
is a breath of fresh air, after which I finally realized just how much I hate working with
sbt
.
Donāt be afraid of nightly
Though Rust is surprisingly mature for such a young language, itās still a very quickly moving target. Thereās a new
stable release seemingly every week, though in fact I think itās more like every six or eight weeks. Getting the new
hotness requires only a rustup update
, and in the stable
channel nothing has ever broken for me after an update. In
2018 the new ā2018 editionā of Rust was released, which was the first edition since ā2015ā. The 2018 edition added some
new functionality that wasnāt entirely backwards-compatible, but a tool called rustfix
handled the changes
automatically for me.
That said, the Rust nightly
channel is an entirely different beast. As the name implies, it is built nightly, and is
where all of the new, experimental, unstable, and also the most cool features live. In my case, Iām writing a lot of
async I/O code for which the forthcoming async/await
syntax is critical. This is not stable, itās only available in
nightly
, which means I have to build my code against nightly
rust. This isnāt ideal, but nor is it a deal breaker.
It just requires a bit more understanding of what is going on. For someone just learning Rust my advice is to stay on
stable
, but if you run into something that requires nightly
donāt automatically assume itās too unstable and broken
for you to work with. Itās easy enough to rustup install
nightly, and equally easy to go back to the warm maternal
embrace of stable
if nightly
is too dark and full of terrors for you.
All the cool kids are doing it
Iām not one to be a slave to fashion, however Iām surprised how often Iāll start using a tool only to discover that itās written in Rust. Hereās a partial list of fantastic open-source tools built in Rust:
alacritty
- My favorite terminal emulatorripgrep
-grep
alternative thatās absurdly fastbat
- A smarter fastercat
that does syntax highlightingfd
- Stupidly fastfd
goes great withfzf
actix-web
- A Rust web app framework with very impressive TechEmpower benchmark resultsLanguageClient-neovim
- The Vim plugin I use to add LSP support to Vimstratisd
- RedHatās new storage management system for RHEL, replacing Btrfs with something that presumably wonāt eat your data
Summary
How would I summarize my experience with Rust six months in? Thusly: ā¤ļø
Donāt get me wrong, Rust still has rough edges. I often run into something that doesnāt work, for which the answer is āoh yeah that should work, once (INSERT RFC HERE) lands, which BTW might not ever happen but in any case wonāt be soonā. Sometimes the compilerās pedantry still aggravates me. Sometimes I find the unit testing support to be primitive. And itās definitely not the language which allows for the fastest prototyping of ideas, though itās definitely a contender for the language which produces the fastest production-ready implementation.
But all languages have their limitations. After venting oneās frustration about some limitation or bug or design decision, the important question to ask, and to answer, is āwould I rather be using another languageā? For me, now, the answer to that most important question is always āno!ā.
In fact, I havenāt enjoyed working in a language this much since I started to play with Scala back around 2012. I expect that Rustās popularity will only continue to grow, and that more members of Go projects will look longingly at Rust projects and wonder what might have beenā¦