Rust and Go vs everything else

Rust and Go vs everything else

Rust and Go make perfect partners

This is an edited version of a recent interview with me by Alex Pliutau, for his excellent packagemain.tech newsletter. Thanks, Alex!

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 [including The Secrets of Rust: Tools].

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?

Anything’s hard if you learn it the wrong way. In my book The Secrets of Rust: Tools I take a slightly different approach to most Rust books or tutorials. Instead of trying to tell you everything about Rust the language, which is a bit much for beginners (and we all start as beginners), the book guides you through writing a number of useful programs in Rust.

What I’m aiming to show is how much you can do in Rust without even knowing about certain features. If you write simple, straightforward programs that glue together existing crates, you can probably solve 80% of problems with only about 20% of Rust.

The “minimum viable Rust” strategy, maybe.

Exactly! Once you’ve built some confidence in the fundamentals, though, and you’ve started to think the way the Rust compiler thinks, you’ll be ready to meet some more advanced features. As the book goes on, it introduces neat things like generics, traits, and so on.

The point is that Rust is a language with lots of features, but you don’t need to use all of them all of the time, and certainly not while you’re learning. If you take this approach, there’s no longer such a big difference between learning Rust and learning Go.

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 would be 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.

All the best Rust books also include lots of examples, and Programming Rust in particular is full of interesting code.

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. The language has strong opinions about everything, so you don’t have to. Just copy and paste a bit of code and you’re basically up and running.

Rust is even more radical, though, in a different way: its design forces you to think more carefully about what you’re doing. The compiler constantly guides you towards a version of your program that’s safer, more efficient, and more likely to be correct. Once you get the hang of thinking about problems the Rust way, it’s hard to go back to what you were doing before.

I think a working knowledge of both Go and Rust is essential for anyone who sees themselves as a software engineer in 2024 and beyond. That’s not to say that skills in other languages aren’t still valuable: of course they are, and will be for some years yet. But if you’re learning a new language, and that’s always a good idea, Rust and Go are great choices for the future. Between them, they neatly cover the whole range of things people want to write programs about.

Go is perfectly suited for plain, ordinary, everyday software: command-line tools, business processes, database applications, web services. It’s fairly easy to write Go programs; they build fast, and they run very fast. For probably 80% of the software we write, Go gets the job done just fine.

Rust, on the other hand, neatly fills the gaps where Go isn’t an ideal choice: kernels, firmware, embedded devices, real-time systems. It gives you all the keys to the hardware, and to ultimate performance. Its relentless focus on memory safety and program correctness make Rust the obvious choice for safety-critical applications: industrial, medical, aerospace.

And elevators, of course.

Naturally! So I like that aspect of Rust, and it’s also just a really fun and expressive language. It has all the toys! Anyone who got into programming just for the sheer fun of it will appreciate Rust. A well-written Rust program is a true work of art.

Don’t be put off by the initial unfamiliarity of the syntax, the struggles with the borrow checker, or the feeling that the compiler has it in for you. It’s teaching you a new way of programming, and while learning doesn’t always feel good in the moment, you soon start to feel the benefit of it. Rust has wonderful ergonomics: little things that make life easier for the programmer, like options and results.

Even if you decide that Rust isn’t really your thing, you’ll still have learned some interesting new ways of thinking about problems, and it certainly also helps you understand more about the different trade-offs that Go makes.

I wrote a more detailed comparison post about the two languages at Rust vs Go, explaining the different ways that they solve problems, and what sort of tasks each of them excels at. And I think that from a career point of view, both Go and Rust will be very valuable skills for the foreseeable future. They’re the two programming languages that I advise everyone to learn first. It’s not really “Rust versus Go”, so much as “Rust and Go 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.

 
$34.95
Add To Cart
 
Suite smells: undertesting and overtesting

Suite smells: undertesting and overtesting

Constraints in Go

Constraints in Go

0