Skip to content

Instantly share code, notes, and snippets.

@evaporei
Created March 20, 2025 18:51
Show Gist options
  • Save evaporei/9b9b6df8a02f69af24459ea25065b789 to your computer and use it in GitHub Desktop.
Save evaporei/9b9b6df8a02f69af24459ea25065b789 to your computer and use it in GitHub Desktop.
Rust: The Good. The Bad. And The Ugly

Rust: The Good. The Bad. And The Ugly

I have been programming Rust for 6 years now (since 2018 edition). I have also done professional work with it for 4 companies at this point: one for “embedded” (payment terminal protocol), blockchain, web backend services and also video/audio streaming (WebRTC).

I reached a point where the honey moon phase is over, and I have a more holistic view on this programming language.

The Good

Let’s start with the good stuff. Rust has great learning material and it has a rich ecosystem of libraries at this point, covering the most common developer use cases. It is usually a great experience to build and run Rust projects. You can clone a repository, and cargo will do everything for you. 97% of the time you don’t have to deal with linking issues,Makefile s etc, that usually happen in the C/C++ world.

I also think it makes systems programming more accessible and allows for non low-level programmers to achieve better performance and use higher level constructs without resorting to an interpreted (JS, Ruby, Python, etc) and compiled (Go, Java, C#, etc) languages.

Although in the beginning it’s hard to learn the borrow checker way to make your programs compile, within a few months you should be able to grasp how you can achieve your goals, either being with .clone()s or some other way by using the smart pointers in the standard library.

Having programmed in C, I have seen over and over again the Rust compiler catch bugs early, and I genuinely think what they’ve achieved with it is quite impressive. And I also feel more safe working and merging Pull Requests in a team that uses Rust compared to C or even TypeScript, it feels like there are less things to break.

This safety is also very relevant when doing refactors. It’s the complete opposite experience of refactoring in a dynamically typed language, it’s almost like everything is being checked and should be working like before, for the most part.

For sure, the most loved part is its type system, everyone that likes Rust misses its types when they program in different languages. The standard library is also very well designed in my opinion, I never had any problems with it and I don’t know how I could do it better within Rust’s constraints.

The Bad

Probably the most complained one on the internet are the compiled times. Although in modern computers, such as a Macbook Pro M4 you can compile 50k lines in 2 minutes (real project I’ve worked on), that number goes way up on older/slower machines and bigger projects, eg: 15 minutes for a release build in a real axum + diesel project. Although rollbacks are possible, this makes publishing a quick fix to production impossible.

Another issue is the size of the target folder (where your Rust build & executables live). If you work on a project with a few dozen dependencies, you can expect it to reach dozens of gigabytes. For reference I have a friend that works in a 500k line codebase, and he cleans his folder every week, since it reaches 100GB quite easily.

Those two make me a bit sad on the accessibility front. You must have a good computer in order to program in Rust. And there’s no low hanging fruit to improve these problems for now, considering that the compiler keeps getting more complicated with more features, and the build sizes are partly there to make compile times faster (by caching). They’re also a consequence of using LLVM as a backend, their debug builds are a mess (you can have stack overflows quite easily, for example).

Looking from the code perspective, programming Rust in a large project often feels like working in a big OOP codebase but with more safety guarantees. Although inheritance is not a feature, Rust programs are usually built around having objects that have opaque state, and send messages to each other (methods), so you end up having a similar experience. Specially given that traits (interfaces) are quintessential to the ecosystem and language. It’s quite common to go to definition in your editor quite a few times to actually get to the real implementation, or it’s scattered across many objects. There are ways to write simpler Rust, but it’s not always possible, and it’s not what you’ll mostly encounter in the wild.

It is also important to note that, although you don’t have to manually do malloc and free to allocate and deallocate memory, the complexity of doing that is converted to completely rethinking how you layout your memory. Often the difficulty of writing Rust programs is how to represent data structures that can be safely acquired and released, by following tough compiler rules.

And The Ugly

Because packages are so easy to include in your project, all popular and useful projects in Rust easily reach hundreds of dependencies. Yes the compiler should fold some of that in the final executable, this is technically what they call zero cost abstractions. But these abstractions come at a cost of understanding, and can also be an attack vector as we’ve seen over and over again in the JavaScript and Python communities.

Compiler errors can also be a nightmare depending on the crates you use. Diesel (Rust’s most popular ORM) tries to make many guarantees at compile time, at the cost of good compiler error messages when you make a mistake.

Asynchronous programming takes complexity in Rust to a whole new level. I have yet to find a person I know that actually understands how Pin works and can explain it. I know several great Rust programmers, and they have concrete understanding of parts of it, but the full picture is always hard to paint. The mechanisms in the language to make this work, although technically impressive given the ownership, borrowing and lifetime constraints, are extremely complex. I honestly recommend you just use bare threads if possible and avoid async in Rust.

Last year I delve into the inner workings of data structures and algorithms, and honestly Rust felt very cumbersome for solving these types of problems. For those who are not aware, many of the base structures in the standard library depend on the unsafe construct to actually build performant and useful data structures. And unsafe Rust is not ergonomic at all, perhaps by design, so you avoid it.

Recently I have been exploring graphics programming, by making games and graphical applications such as a text editor, etc. These applications often need a shared global state. Rust is very bureaucratic about that, since it assumes multi threading by default. It is also common to see such applications being built around one single struct where all methods reside. This complicates more than helps, compared to it’s C counterpart where you can access globals freely in standalone functions.

Rust ensures safety at the cost of making simpler programs hard to create and understand. And everything that has grown over a few thousand lines will experience many unnecessary traits and indirections.

Conclusion

Rust is easy to get started and has a vibrant community and rich libraries. It’s also a good replacement for where C++ would be a fit. However it does come with trade offs, specially lying in its complexity and compile times.

@evaporei
Copy link
Author

This is just a draft made in a couple hours, I asked for feedback from a few friends and they asked for examples and more detail.

There's probably a way to tell a good story with this, for now I'll leave it as it is in this "secret"ish place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment