Should you Rust, or should you Go? Which language is better, and does that question even make sense? Let’s talk about Go versus Rust in 2024, with our special guest, John Arundel. John is the author of For the Love of Go, Cloud Native DevOps with Kubernetes, and many other books. He also teaches both Go and Rust, so it’ll be interesting to hear his perspective. Here’s the interview!
John, we seem to be hearing more and more about Rust these days. Can you give us a quick introduction? What is Rust for, and why does it exist?
Sure, that’s easy. Rust is a language for controlling elevators.
Are you kidding?
Not at all. Graydon Hoare, the originator of Rust, got frustrated when the elevators in his building kept breaking down due to software problems. He thought “Surely we can do better than this!” And the rest is history.
We can’t prevent all bugs, but we can at least use a programming language that eliminates some major categories of bugs, such as buffer overflows, data races, and “use after free” issues. So from the very beginning, Rust’s focus has been on building reliable software, automating many of the safety checks that good programmers do anyway, and helping to catch mistakes before they reach production.
That sounds like a good idea, but I get the impression that Rust is a hard language to learn—especially compared to Go. Is that fair, and if so, why?
Go and Rust are both trying, in different ways, to solve the same problem: it’s hard to write software at scale in traditional languages like C and C++, because the programmers have to be very experienced and knowledgeable to avoid making mistakes. Even experts can slip up from time to time, and it takes a long time to train programmers to become experts.
Go tackles this problem by radically simplifying the language: compared to something like C++, it has a lot less syntax to learn. So programmers can spend their time learning to write programs well, rather than mastering a big, complex language. Go is lean, but effective.
To use an analogy, it’s easier to learn to drive a tractor than to fly a spaceship. The tractor may be a humble, pragmatic machine, but it does its job perfectly, and it’s actually a better choice than the spaceship for many tasks: ploughing a field, for example.
I like your analogy. I guess Rust is the spaceship?
Yes, Rust is big, complicated, and powerful, combining many of the best ideas from traditional imperative languages like C with functional programming concepts borrowed from languages like Haskell and Lisp.
There’s more to learn in Rust than there is in Go, but then it does more! If you want to fly to Mars, a spaceship is a better choice than a tractor. Of course, it takes a little longer to train an astronaut than a tractor driver.
Go has built-in garbage collection, which is great for simplicity. How does memory management work in Rust, and is it a big challenge to learn?
Yes, garbage collection means you don’t have to worry about allocating and freeing memory yourself, as you do in languages like C++. That makes programming easier and eliminates all sorts of memory-related bugs. On the other hand, you need a relatively complex runtime, and garbage collection affects performance.
Rust takes a different approach. It reclaims memory automatically, but without having to pause the program. It can do this by keeping track of all the references to a particular piece of data that exist. When no part of the program can refer to the data any more, Rust knows that bit of memory can be safely recycled straight away.
Yes, I’ve heard Rust has a strong focus on ownership and borrowing. How do these concepts compare to working with pointers in Go, and what are some good ways to wrap my head around them?
Well, I have good news—if you’re already used to pointers in Go, then references in Rust work basically the same way, only safer. If you create a mutable reference to a variable, it works just like a Go pointer: you can pass it to a function, or store it somewhere.
But, unlike in Go, as long as that mutable reference exists, it has exclusive access to the data: no one else can modify it, or even read it. In Go terms, it’s like having an automatic mutex lock. And when a function doesn’t need to modify the data, it can instead borrow a shared reference, which is read-only, and lots of them can exist at once.
Rust also keeps track of the original data: when it goes out of scope, any references to it are no longer valid. So the compiler can detect many kinds of dangling pointer bugs where you try to use a reference to a value that doesn’t exist any more. That results in undefined behaviour, which is a nice way of saying that something horrible will happen, and part of Rust’s value proposition is “no undefined behaviour—ever”.
In Rust, then, we have to figure out a way to write our programs so that references to data are always valid, and only one mutable reference ever exists at a time. That takes some getting used to (Rust programmers call it “fighting the borrow checker”), but the resulting programs are more reliable, and more likely to be correct.
For example, all Go programmers are familiar with data races, where two or more goroutines try to access some shared data at the same time, with unpredictable results: at best, the program will crash, and at worst, it will continue with corrupted or invalid data.
In Rust, a program like this won’t compile! The ownership and reference rules mean that two mutable references to the same thing can’t exist simultaneously. You just have to solve the problem a different way.
That brings us neatly on to concurrency. I like Go’s concurrency features with channels and goroutines. How does Rust handle concurrency, and are there any similarities I can leverage from my Go experience?
Yes, goroutines and channels are great: a super-lightweight task abstraction that’s very cheap compared to traditional multithreading. On the other hand, Go only gives us the fundamental building blocks: it’s up to us to make sure we use them safely, avoiding data races or deadlocks. And that can be hard to do!
Rust doesn’t have goroutines, but it does have async tasks, which are much like goroutines, only with the usual Rust safety guarantees. There are also some excellent third-party frameworks such as Tokio and Rayon that can just take a bunch of data and automatically figure out the most efficient way to crunch it in parallel.
So, while concurrent programs will always be difficult to write well, if you can do it in Go, you’ll find those skills transfer well to Rust, too.
I like to learn by doing. Are there any good hands-on exercises or projects you’d recommend for a Go programmer getting started with Rust, like the Tour of Go, for example?
Rustlings is a great place to start: it’s a set of interactive bite-size exercises that guide you through all the language fundamentals. If you want to get feedback from a real live human, check out the Rust track on Exercism. There’s also Rust by Example, which is a terrific resource for working example snippets.
It sounds like you’re a fan of both languages. Which do you prefer, and would you recommend that someone who already knows Go should also try learning Rust?
Yes, Go and Rust each appeal to different parts of my brain. I like the radical simplicity and pragmatism of Go: it does a lot with very little, and solves most problems pretty well.
Rust, on the other hand, neatly fills the gaps where Go isn’t an ideal choice: kernels, firmware, embedded devices, and safety-critical applications such as medical devices, industry, aerospace, and so on.
And elevators, of course.
Naturally! So I like that aspect of Rust, and it’s also just a really fun and expressive language. I think it’s well worth anyone’s time to have a play with Rust, and stick with it long enough to get over the initial unfamiliarity of the syntax, and the struggles with the borrow checker.
Even if you decide that Rust isn’t for you, you’ll learn some interesting new ways of thinking about problems, and it also helps you understand more about the different trade-offs that Go makes.
From a career point of view, both Go and Rust will be very valuable skills for the foreseeable future. I think that pretty soon, instead of “Go versus Rust”, we’ll be talking about “Go and Rust versus everything else.”
John, thanks for being our guest, and giving us your perspective on Go and Rust. Where can people find out more about you—for example, if they’re interested in your books or training courses?
It’s my pleasure! Do drop by my website at bitfieldconsulting.com if you’d like to know more, or get in touch—I’ll be happy to chat.
That was exactly my question at the beginning of the year. Then I bought John's book on Go. Now, I need to find time to play more with Go :D Stop playing Baldurs Gate 3 will definitely help ...