Why Rust? I have a GC!

10 January 2020

My $dayjob is working in the cozy realms of Java, coddled by a GC and the amenities of a fat runtime. Common wisdom is to use a GC if you can afford it. I sometimes even write python if performance is a non-issue. Why would I then long for Rust every now and then?

Because it’s not just about the garbage.

Rust gives me an edge in speed. Autovectorization, flat data layout, high memory locality and a lean & mean standard library translate to C-like runtime performance. The JVM is a marvel of engineering, but even so, it cannot outrace even moderately tuned rust code. Also even with great frameworks like JMH, it remains positively hard to measure performance of Java code at lower scales.

But it’s not about the performance either.

Rust gives me a stronger assurance that my code is actually correct in the face of concurrency. Java can only do so on a best-effort basis, throwing a ConcurrentModificationException if it detects a data race (in the best case). And nowadays, everything has to be concurrent at some point.

Even if I code single-threaded, a library I use may use threads or tasks to make good use of my CPUs. A good example is logging, where the contemporary libraries all use async loggers for maximum speed. More than once, my code was stopped by a ConcurrentModificationException because I tried to log some collection that was modified later in the code. The async logger had deferred the formatting (and thus iterating the collection) until the modification was already underway. What’s bad about those errors is that they may only happen in a few percent of cases (depending on highly variable timing), so the error may appear late in the program’s execution, which translates to wasted time and energy.

In safe Rust, the compiler would have stopped me from even running the program.

Java has a well-deserved track record of being a good language for “programming in the large”, but it has no way of locally reasoning about potentially concurrent code. This means that distant changes, when combined, can trigger failure, perhaps not as catastrophic as with C or C++, but still likely hard to track down, and thus costly.

But it’s not about the safety either.

Neither Java nor Rust are good languages for code golf. Still, Java often requires more ceremony than Rust, or offers less syntactic power. Rust’s powerful type system lets me eliminate many of the duplications I have to do to keep Java happy. And macros (which I sorta-kinda can emulate with hacky bytecode generation, but that gets even more unwieldy quickly) get me the last five percent of cases. Also, the aforementioned assurances let me front-load more error fixing in my code-compile-run cycle, cutting debugging time considerably. Thus, Rust can arguably make me more productive than Java (or not, depending on the task).

That said, there are still quite a few reasons to use Java nonetheless. IDE support is still unmatched. The library ecosystem and community are much larger which translates to less risk for my employer.