Wednesday, January 1, 2020

The rise of Rust

The Rust programming language has been increasing in popularity of late. I learned to program in Rust in order to teach my Spring 2017 operating systems course using the language, inspired by the work of David Evans at the University of Virginia. I was so pleased with the experience that I taught the course in Rust a second time in Spring 2019. I have also started using Rust in software development in my research program.

There are a few things I have come across recently that, I think, demonstrate the appeal of programming in Rust. The following quotes come from a Reddit thread posing the question: "Why does Rust seem to inspire people?"
It took me literally a day and a half from "I'm going to try writing my first Rust program" to "here's a working multithreaded network program with string processing that can pump out 100GB+ a day of throughput with no memory leaks or buffer overflow errors." That's nuts. I couldn't have done that in a GC'd language or C++, or at least not as quickly. The way move semantics, the borrow checker, thread spawning, and channels/atomics work is very well thought out.
I can vouch for this experience personally. In fact, writing a multithreaded web server is one of the key assignments I employ in my operating systems course. The ownership guarantees embedded in Rust's static type system make this possible.
I’m a college professor in software engineering, so I’m saturated in young people excited about tech. For the last few years, it’s been fascinating to see how excitement for Rust has gained momentum.
In our major, you tend to learn C freshman year alongside Python, Ruby, and Java. Lots of folks love C for being system-level and fast. But, pointers and malloc are brutal to learn. I witness it every year. Even if you enjoy C, everyone had a story about staying up all night debugging a segfault. I recently met with a mental health counselor who sees a lot of students in my major. Segfaults came up. Yeah. Anecdotally, C’s memory management hurts peoples’ mental health.
Then, when they take my security course and learn about buffer overflows and other memory corruption issues they start to question the wisdom of such permissive language like C and C++. It’s a phase we all go through. We all question the establishment in college.
Then when students learn about Rust, they hear about how memory allocation is safe and zero-cost abstraction. They get excited and start evangelizing to their peers. The excitement is noticeably more than other tech, which I always thought was interesting. We don’t cover it in any class (yet...), so they pick it up in their spare time.  
I’m honestly surprised that a lot of my students like Rust. (Thrilled that they are!) Its a tough language to learn. It’s got some different concepts (e.g borrowing, lifetimes) that are a big paradigm shift for them. For me, I rolled my eyes about it for like three years and just recently started learning Rust and I’m impressed.
It really is time to question the establishment. The experience of programming in Rust demonstrates that you can write bare-metal code in a language with a type system that guarantees the absence of buffer overflows and memory corruption without compromising on performance. At this point, it is arguably an ethical imperative to show our students that there is a better way.
The sad reality of our world is that everything is broken - as someone who has done security research, I can tell you that every word in that article is true, and more.
The reason why everything is broken is because there are two kinds of programming languages: the slow ones (Python, Java, Go) and the ones that lead to horrific security vulnerabilities (C, C++). We need our computers to go fast, so we use C and C++, and so everything is glitchy and broken.
Now imagine a language that breaks that mold. That is fast as C/C++ and can fully replace them, but without the security vulnerabilities. The holy grail that would lets us write code that is secure, reliable and fast all at the same time.
That's Rust.
I seriously cannot overstate how big of a deal this is. Nothing like this has ever existed before. Most thought creating such a language impossible!
This reinforces what the professor had to say, from the viewpoint of a computer security researcher. Rust is a tool that suffices to make legions of computer security problems just go away.
Five days trying to figure out a segfault. (Personal project in highschool) Gave up. Half a year later, checked it out again. Blindingly obvious after ten minutes. Even experience doesn't beat the three seconds it takes to read the rust compiler's output. Heavenly.
This quote comes from a manifestly talented and experienced developer. It is interesting to think about the useful experience this person could have developed in solving problems other than finding segmentation faults if, in his career, the compiler had been a reliable ally in avoiding these problems.
Rust is far from perfect, and sometimes feels a bit pedantic, but coding in Rust I never ever put my hands in the air and think "damn, I hate this language so much, it's so stupid", which I routinely do when using other mainstream languages.
Rust is the first language I am actually pleased to work with, and it's been years now. Tooling is great, community works well, popularity is good enough. And the language feels like it was coherently and carefully designed for humans (as opposed to: oh, you have a bug, you must be stupid or something for doing wrong things). And it is a programming language suitable even for low level systems/kernel/embedded programming, just as much for web dev, which is simply amazing.
I love the core features: ownership/borrowing, thread-safety, good type system, and miss them soo much when I can't use them in other languages. It's a very powerful force, reinforcing the appreciation of Rust.
So, everything I do in Rust ... feels joyful. I feel like you can achieve anything, and it is not even that difficult. At least the language empowers me, instead of getting in the way, like other ones.
A couple of comments:
  • I boldfaced an important line above, which references the common notion that memory bugs are signs of bad programming. The Rust environment eliminates this needless hazing. 
  • The second line I boldfaced reflects my own experience, but I want to include an important caution: it takes quite a bit of practice with Rust to get to the point where it is not difficult to use. The language is well-known for its steep learning curve. I would like to encourage anyone starting out with Rust to persevere until you get to that point.
The article Why Rust is the Future of Robotics also has interesting insights, most particularly the following information about embedded development:
For embedded systems, specific tools have also been developed:
  • Peripheral control — There is this thing called svd2rust that can automatically generate a Rust API to access every peripherals on a micro-controller, directly from it’s SVD description. Rust is smart and it can enforce at compile time that you use peripherals as they should be. You cannot compile if you try writing to a read-only register, or read to a write-only registers. You cannot write invalid bit patterns to a register too. The SVD can define a range of valid values and Rust won’t let you go out of range.
  • Ressource collision prevention— In the next version, they will introduce singletons to let Rust know when some code wants to use a peripheral already in use, for example a timer. This is a common source of problems in embedded systems were resources are limited and several devices might want to use them. It is a classic with Arduino Uno boards that only have few timers. So when you use the Servo library, you cannot use PWM on the pin 9 and 10. But the code will compile, push to the board and fail badly by producing very strange behaviours extremely hard to debug. This made manymany, Arduino users confused. With singletons, Rust can tell you at compile time if the timer is already in use, preventing users massive headaches without any runtime overhead.
That is, it turns out the ownership concept can be employed to avoid many specific hardware-utilization errors at compile time. Here is a similar comment from a Hacker News thread:
As for the borrow checker, I actually did find it useful in bare metal. But more for handling hardware resources. Different measurements require different sets of power supplies to be activated, and exclusive control over different I/Os, etc. The ownership model made it easier to ensure statically that we sequence the measurements in a way such that the different measurement routines can never reconfigure resources that are in use by a different routine.
In effect, the borrow checker is proving to be a highly configurable theorem prover for software verification!

The ownership concept also aids in code optimization. Amit Patel describes how, thanks to knowing the owner of an array, Rust code for an array copy uses 50% fewer memory accesses than the equivalent C++ code.

For those die-hard C programmers convinced that nothing can beat it, I recommend reading Learning Rust the Dangerous Way. The author's methodology is fascinating. He took a benchmark C program from the Programming Language Shootout and translated it into Rust.

An important aspect of Rust is that code can be marked unsafe, indicating that the compiler's safety guarantees won't be enforced on that block of code. This transfers the safety burden from the compiler to the programmer. It acknowledges that there are situations where the programmer really knows better than the compiler. What is great about this is that by eliminating safety checks on the smallest possible block of code, the bulk of the program still retains safety guarantees, making it easier to reason about its correctness.

This author, then, takes the C source code for the n-body problem and transliterates it into an equivalent Rust program, the entirety of which is in an unsafe block. He then gradually refines each unsafe block into safe Rust code. At the end, the only unsafe code remaining is the calls to the CPU vectorization instructions. As of this writing, the fastest program in the shootout for this problem is Rust #7, by a different author but taking the same basic approach.

The significance of this achievement is to end the belief that unsafe code is generally necessary for high performance. For the most part, except for small, specialized routines, it is not, and most (if not all) of any given program can genuinely be checked at compiler time to be safe.

Although there are not necessarily a huge number of jobs available in Rust as of yet, I suspect this is about to change very quickly. Here's one example. A student of mine was recently hired at Google, and his group was ecstatic that he learned Rust in my operating systems course. While the group does not yet have Rust in production, they are looking for an opportunity.

As talented programmers begin to insist on using Rust for the sake of its safety guarantees, I predict that companies will respond to this pressure by starting more and more projects in Rust. I expect it to be a top-10 programming language by the end of this newly inaugurated decade. I highly encourage anyone interested in these possibilities to take the time to learn to program in Rust. I think this language is one of the most important technological advances in computing from the past decade, and now is the time to embrace progress moving forward!