6 min read

The Writing on the Wall: Get Rusty

Introduction

I'm a software engineer of 10 years, working in the computer vision & image processing domains, working mostly in C++ and Python. In that time I have learned a great deal: everything from high-level concepts about what it means to "be an engineer," to using a CPU mailbox to write a diagnostic dump to flash on a failing embedded device, to mentoring an intern well, to cutting-edge image-formation techniques for SPAD sensors, to porting 25-year-old code bases so that they run on Webassembly, to interviewing well (on both sides), to using mountains of preprocessor directives and specific ways of constructing your inner loop, just to convince the compiler to auto-vectorize (because hand-written intrinsics are so 2009). As I write all that, I feel validated that at least I didn't waste the last ten years of my career. Oh, and I just finished my MS, so that's neato.

But over this last ten years, I've developed something akin to Stockholm syndrome for C++. It's what I know. Despite it having parody sites like the FQAs, it's what gets the most performant results. It gives you the most control. Everywhere that performance matters, you will find it, including in most other programming languages (which bind to C/C++ code when performance really, really matters: I'm looking at you, numpy). It's the samurai sword to Java's billy club: sure you can gut yourself, but if you know what you're doing, you'll be unstoppable. Right?

Sure. But does that mean there's not a better way?

For my very last class of grad school I decided to take a course using Rust. I'd read some about it, watched a video or two, and written a "hello, world!" But other than that I had never engaged with the language. So I feel I am something of an interesting test case to help answer the question: how painful is it for a C++ developer to ramp up on Rust? What can you actually learn in ten weeks?

In this article, I share my experience learning Rust, including writing a simple REST API, and publishing my first crate.

Learning Rust From Zero

Motivation & Medium for Learning

When registering for my last quarter, I put a lot of thought into what I wanted to take: what would give me the most bang for my buck, complement my existing knowledge and experience, and still push me to learn something new?

In the end, I decided to take Bart Massey's Rust Web Development course, for a few reasons:

  • I'd just taken an audio-editing course from him and enjoyed it quite a bit, both for what I learned, and for how the course was project-oriented & not overly prescriptive in terms of structure.
  • I wanted to learn Rust for reasons I'll get to below.
  • I already knew the basics of web development, so I could use the course primarily to focus on learning the new language, instead of trying to learn two things at once.
  • Bart is a big advocate of Rust, which I learned while taking his audio-editing course. And you can learn a lot from evangelists if you care to.

Why learn Rust when you know C++?

C++ and Rust are often put into the same bucket of "crunchy" languages. Why? Well, the fact that both (can) use LLVM might be a part of it. When written well, and purely (no bindings to other languages), both have similar performance. Is it a waste to learn them both?

Aside from the fact that you can't know too many programming languages, I saw the writing on the wall for Rust one day overtaking C++. Maybe not any time soon, but it's just a matter of time if you ask me. Why?

Here are a few signs:

  • Windows is re-writing its kernel in Rust.
  • Linux 6.1 supports Rust and it is already getting use.
  • At Wasmcon in Seattle last year, pretty much the only thing folks wanted to discuss was WASI and Rust. Any mention of Emscripten or C++ was basically met with cringes. There may be some selection bias there, but this sat with me since it happened.
  • Through my connections to the embedded-systems community I have heard a lot of chatter about Rust being adopted for safer system software, and with some bold, compelling claims about its safety and performance.
  • A lot of the best practices I've learned for writing good C++ code are either built into Rust itself, or into cargo.
  • This was a big one to me: A lot of the recent changes in C++ and its standard libraries have basically been to play catch-up with other languages, including Rust. While this also makes it easier to stick with C++ (after all, there's nothing it can't do that other languages can!), to me it's a clear sign that C++ is no longer leading the way (if ever it was).

And as icing on the cake, the Rust ecosystem is relatively new, so there is a lot of opportunity for someone like me, who has some experience and domain knowledge, to perhaps contribute to improving that ecosystem! I've tried to do that in C++ but always wind up finding that someone beat me to my idea a decade ago.

Given all that, I was ready to dive in head-first.

Course Project: Image Tiling REST Service

Without going into too much detail, this was a frontend/backend project, where both ends are written in Rust. The backend runs as a plain old compiled backend service, and the frontend consists of generated HTML that automatically binds with compiled webassembly code. In other words, no (hand-written) Javascript.

It is a service allowing the user to load images into their browser (client side), and view them at varying zoom levels. The zoom algorithm utilizes a computed image pyramid to maximize sampling speed and preview quality,

Code can be found here.

There's not much to say: the tooling is easy, and the ecosystem provides all the stuff you expect out of a modern language:

  • Does away with archaic notions like org-specific "style guides" by standardizing on one
  • Compiler provides thorough and useful error reporting
  • Tons of hands-on guides and tutorials out there
  • Excellent and simple package management (always built from source!)
  • Built-in toolchain management
  • Makes it hard to shoot yourself in the foot

Impressions: What are the big deltas?

Overall, my big takeaway is that the compiler is a slog, and getting things to compile is often a struggle... but successful compilation is a much bigger promise of correctness than C++ code compiling successfully.

And, frankly, a much bigger promise of testability, since the borrow checker and type system have some specific requirements in order to prove no UB at compile time.

Those two things are huge, if you ask me. It shifts left a lot of the software development costs, making things like maintenance a lot easier (I predict). This comes with the trade-off that initial investment will probably be a lot higher. I, at least, wound up in a pattern where I would constantly compile and re-compile to basically get feedback on my code, as I wrote it.

And not trying to toot my own horn here, but my first crate (which I mention below) worked correctly at every increment, once I got it compiling. That happens sometimes when I work in C++, but more often than not I made some kind of stupid human mistake (being, you know, human). That's why I'm such a big believer in test-driven development: it's what I personally use when writing my code, to catch my own mistakes. Not saying Rust development wouldn't benefit from TDD, but the compiler winds up preventing most of the issues that I personally would catch by using TDD.

Other big deltas from C++ that are worth calling out:

  • Type system - in some ways very similar to C++, but winds up being more flexible. That said, I ran into some issues that I will elaborate in another post.
  • Macros - the most arcane part of the language, if you ask me. But extremely powerful. Anything C++ would do through template metaprogramming, Rust does in macros.
  • Borrow checking - annoying at first, but my favorite part: proving memory correctness and eliminating a whole class of bugs at compile time is a big brag, and, from my perspective, worth the initial pain
  • More upstream work

My First Crate: image-pyramid

Implemented as part of my exploratory project, this crate generates image pyramids. Here it is!

Other "aha" Moments

This is worth a read:

Taking Advantage of Auto-Vectorization in Rust
Recently on a project I wrote some audio processing code in Rust. In the past I’ve used C++ to write audio processing code for situations where performance was critical. I wanted to take that C++ optimisation experience and see what is possible using Rust.

Conclusion

C++ is going nowhere soon, and Rust's ecosystem is not mature enough to replace it – evidence of this is the fact that most Rust libraries for one of my domains (image processing) are, at the time of writing at least, just bindings back to C or C++ code.

That said, anyone who considers themselves a seasoned C++ developer, or wants to be, would be well helped in their career (for both career longevity and mobility), to learn Rust and stay up to date with its developments.