Rust. An Opinion On The Good, The Bad, and The Ugly

TL;DR Rust is a necessary pain in the butt that will ultimately make the programming world a better place

Less than one year into my career as a programmer, I watched a senior developer, Mike, write some C code. Mike was always ultra productive and wrote simple, easy to read code. His skill and experience were evident in his work. He was the lead coder at the company, and for good reason.

Then he did something that, to my inexperienced eyes, was downright dirty. He used “goto”. I knew about goto statements. They were ugly. They created spaghetti code. Using them was bad programming practice. Every article I had read said so, and my professors agreed. There was always a better way to solve a problem than using “goto”.

In the almost 20 years since I’ve used goto statements a lot, most recently earlier this week. I learned there’s a time and place for everything. Programming languages are just tools. The skill and ultimately the responsibility for creating good code lies with the coder, not the language. But there’s more to it than that …

Enter Rust

Rust solves many memory problems. In many ways, you can’t have bugs due to bad practice because Rust forces good, reliable programming practices. Deviate from that norm and you need to either explicitly mark your code as “unsafe{}” meaning you take responsibility for your bad behaviour, or your code will not compile.

This is the good, arguably.

Rust’s borrow checker ensures you can’t make certain types of errors. As the size of a project grows, whether in terms of complexity, number of lines of code, or even coders, the borrow checker ensures a level of consistency in your code that would be exceptionally difficult to achieve otherwise. Put differently, using Rust instead of, for instance C, ensures my malloc() will not be free()’ed twice when my initially humble hobby project develops into a 12 developer 500k LoC, multi module behemoth 4 years later. The compiler has you covered.

One of the cons of Rust, generally speaking, is the long compile times. Personally I don’t have an issue with this. And the reality is the incremental compile times are not long, only initial and rebuild times. When weighed against the pro of having the compiler save me potentially hours, days, or weeks of future maintenance and bug fixing, a longer than normal compile time periodically is hardly an issue.

Language complexity is far more of a sticking point for me.

I found Rust to be large, complex, and generally difficult to learn. There was a point several months ago when I seriously questioned whether the effort of learning Rust and fighting the compiler on every build was worth it. The answer was usually no.

The Rust learning curve is extremely steep, especially when coming from a more conventional language like C or Java. I suspect this more than anything will prevent wide scale adoption of the language amongst existing coders. But new coders who know Rust as a first or at least one of their first few languages, will find the language much easier given that they simply learn things the Rust way instead of needing to unlearn years of bad habits first. The impact of this will be older code maintained by an older generation with older ways of thinking. The newer generation of coders by contrast will create more reliable, and generally better code.

You can learn the basics of C in a day and be reasonably productive in a week. I would say the same is true of Golang, Java, Python, and numerous other languages. But I often see posts online, even in the Rust forums, where people struggle with Rust concepts months after beginning their Rust journey.

Other languages are just easier.

And despite the correctness that can be achieved with Rust, in many cases easier is better. Microservices should never be large and complex. So while a microservice can be written in Rust, is the steep learning curve worth the effort? My preference would be Java or Golang. Despite the lack of correctness, the problem is small and simple enough that Rust becomes more of a hindrance. The same applies to just about any piece of code where the focus is rapid development. Rust is not the best tool for every job.

However for larger projects, I am leaning more towards Rust than any other language. The benefits of long term consistency within the project enforced by the compiler are huge. And for this reason alone, I suspect companies will be switching much of their future code to Rust. Having a computer tool ensure you have not made mistakes is immensely beneficial.

Once upon a time developers took responsibility for all code. Understanding the rules of a system were important not only because it allowed you to use the system correctly, but because it empowered you to bend those rules where required. Understanding this meant “goto” statements were not bad … when used responsibly. It meant sharing memory without mutexes was acceptable because 8-bit microcontrollers were not used for multitasking anyways, and in that context race conditions would not exist. These things are not bad if you understand the rules. Automated code checkers, at least the ones that exist currently like the borrow checker, do not fully understand these rules. And so the huge benefits of a language like Rust come at the cost claiming that the compiler, not the programmer, knows best, at least by default. For a large enough problem, I believe this is true, for better or worse. Yet this also turns the art of programming into the the science of programming in many ways. In a way, it’s like watching the end of a computing era slowly unfold.

Ultimately I would say Rust is a tool. An extremely powerful tool, but just a tool. Using it makes for better, more reliable code and it should be used whenever appropriate, but it is not a solution to every problem. Regardless, I still believe it to be the most game-changing language currently on the market.