Needless of a lengthy introduction, Rust is a systems-level programming language that puts "performance", "reliability", and "productivity" at the forefront of its core values. Namely, its most notable language features include memory safety guarantees with the ownership model, a standardized package manager with Cargo, "zero-cost abstractions" with the standard library, powerful multi-threading primitives and idioms, and low-level interoperability with C/C++.
It comes to no surprise, then, that Rust has steadily been on the rise in terms of adoption and popularity. In fact, for the fifth consecutive year, Rust has taken the top spot as the "Most Loved Programming Language" according to the 2020 Stack Overflow Survey.
But is all the hype justified? In this article, I reflect on my experience with Rust in the past year. This includes the golden moments as well as the pain points. Without further ado, let us begin with the golden moments.
The Golden Moments
Straightforward Installation Process
Perhaps the most significant barrier to entry for any programming language is the development environment. A convoluted toolchain is sure to deter anyone new to the language. I learned this the hard way a few years ago when I had to mess with the system PATH
and other environment variables just to compile my first Java program.
Much to my relief, Rust does not suffer from this issue. Thanks to the rustup toolchain utility, the installation process could not be any simpler for a systems-level programming language. Although the Rust compiler depends on platform-specific build systems, this extra step should not be too much of a hassle given that the installation process for these external dependencies are also relatively straightforward. The Visual Studio Build Tools for Windows, for example, provides an automated installer.
Familiar Development Workflow
When it comes to project development, the Cargo package manager makes dependency management and build system configuration an absolute breeze. Noting that Rust is a systems-level programming language, it surprised me how simple it was to get a project up and running without any boilerplate and prior configuration. Quite literally, it just works!
As for dependency management, I felt at home when I pulled in external packages. The workflow was strikingly similar to that in Node.js: step 1, list down the package name in the project manifest; step 2, indicate the version number (with respect to semantic versioning); step 3, use
the dependency; step 4, compile the project; step 5, profit. Indeed, Rust upholds its promise of "productivity" with this simple workflow.
For comparison, this was totally unlike my initial experience with C++. Before I was able to pull in dependencies, I had to dive deep into the rabbit hole of precompiled headers, static libraries, dynamic libraries, linkers, and Git submodules. Needless to say, it was not the funnest experience, especially when I just wanted to hack around the language.
But to be fair to C++, the language does not come with a standardized package manager out of the box. Personally speaking, this is exactly what makes Rust all the more attractive.
Excellent Documentation
Perhaps the most praiseworthy aspect of the Rust ecosystem is its commitment to clear and accurate documentation. Nowhere is this more evident than in the official language documentation. Indeed, the Rust team leads by example in this regard.
In order to avoid having a bloated, monolithic website for all things about Rust, the Rust team separated the language documentation across multiple e-books that cover specific areas of interest. To name a few in particular:
Title | Area of Interest | Description |
---|---|---|
The Rust Book | First Principles | Serves as the introduction to Rust. |
Rust by Example | Applied First Principles | Illustrates the practical usage of the language's features. |
The Standard Library | Standard Library | Serves as the official reference to the standard library. |
The Cargo Book | Cargo | Serves as the official reference for all things about the Cargo package manager. |
The Rust Reference | Full Language Documentation | Serves as the more "complete" version of the Rust Book. |
The Rustonomicon | Unsafe Rust | Serves as the official guide to the "dark arts" of unsafe Rust. |
Indeed, all of these e-books are a treasure trove of information. They strike the perfect balance between approachability, reasonability, and extensiveness. When a topic deems it necessary, the writing style shifts to a drier, more technical tone. Otherwise, the documentation tends to be a playfully engaging reading experience with colorful examples.
Furthermore, documentation is itself a core language feature! Using the doc
subcommand, Cargo can extract all doc-comments in the codebase. It then uses this information to automatically generate static web pages for documentation. In fact, for every package published to crates.io (Rust's package registry), docs.rs hosts the corresponding static web pages generated by cargo doc
.
This extremely valuable feature allowed me to confidently explore and navigate the interfaces of the libraries I use for my projects. To me, the best part about it is the fact that cargo doc
generates the same web page structure for all packages. Eventually, I found myself comfortably accustomed to the familiar documentation format—regardless of the current library. At one point, I even spent almost half a day just scrolling through the documentation for the various methods of each standard collection type in the Standard Library.
Once again, this attests to Rust's promise of "productivity". Although the documentation format may seem boring and repetitive to some, it at least communicates information about interfaces in a standardized manner. Over time, every Rustacean learns how to treat docs.rs as their second home.
Rewiring the Brain to Design Better Software
It is no secret that the Rust ownership model practically makes certain design patterns and data structures difficult to deal with. One of the most notorious examples is the humble linked list. If we were to quickly run a Google search for "rust linked list", we would find a plethora of resources describing the fundamental nuances of the ownership model.
To some, this is an incredible barrier of the language. Personally, I would argue otherwise: the design patterns and idioms of Rust help us rewire our brains to design better software. This is not to say that data structures like linked lists are inherently evil, but there are valid reasons why it is difficult to deal with in the first place.1
With that said, over the past year, I have come to accept the reality that if I end up with an architecture that frequently conflicts with the Rust compiler, there is most likely something worth reworking in my current implementation. This is apparently a common mindset among Rustaceans: don't fight the compiler.
Sure enough, following this advice yielded great results! In one of my projects, I found myself fighting the compiler because I insisted on implementing a callback-based interface, a design pattern that was innocuous in JavaScript. This was fine in many cases, but most of my troubles came from the fact that I required mutable access to borrowed data. I used all sorts of hacks and workarounds (such as the interior mutability pattern), but thankfully in the end, none of them worked.
When I finally conceived of a better architecture for the project2, I realized the truth and wisdom behind the Rustacean Mindset. If I had pushed through with my original architecture, I would have surely ended up with an unmaintainable mess of a project with hundreds of lines of "spaghetti code", all coupled and tangled up in more ways than one.
Instead, I allowed the idioms of Rust to rewire my brain. In the end, I arrived at a better architecture for my project. Thinking like a Rustacean opened my eyes to see better software design patterns that are not necessarily exclusive to Rust itself. Indeed, don't fight the compiler.
The Pain Points
Not the Most Beginner-Friendly Language
I won't sugarcoat anything here: Rust is one formidable beast of a programming language. This invites two possible interpretations. The first of which is that Rust is a powerful language. As for the other, Rust is a painfully difficult language to deal with. Sure enough, both of these are true.
The absolute power of Rust comes from the juxtaposition of its relatively high-level syntax and its low-level capabilities. Compared to its peers, Rust is truly a breath of fresh air. Many particularly cite the verbosity of C++ as a case for Rust's superior expressiveness. I mean, have you seen template metaprogramming code in C++?
Although Rust is indeed more concise and expressive than its peers, it does not mean it is free from shortcomings. The low-level nature of its design makes some design decisions and complexities a necessary trade-off. A prime example is the ownership model and the concept of object lifetimes.
Without going too deep into the weeds, since the beginning, the Rust team envisioned a truly high-level language with performance comparable to that of C/C++. This meant that it was against Rust's core philosophy to impose a language runtime with a garbage collector. Thus, each object in Rust was responsible over their own memory. And hence, the ownership model was born. To put it simply, the ownership model is basically a memory management contract that Rust upholds in order to avoid memory leaks and null pointers.3
This feature alone justifies the low-level power of Rust, yet at the same time, it is also the #1 source of confusion for many Rustaceans according to the recent 2020 Rust Survey. The results show that as many as 61.4% of the respondents agreed that object lifetimes were either "tricky" or "very difficult" to learn.
Indeed, from my experience, this was the trickiest concept to grasp, even with prior knowledge of C++ patterns. Though, to be fair to Rust, this is not entirely the language's fault. Any language that deals with low-level operations inevitably has to introduce complexity into its abstractions.
With that said, I cannot advocate Rust as a beginner's first programming language for the same reasons I would not advocate C/C++. Rust is too low-level to be beginner-friendly. Although it is not the worst language to start with, it is nonetheless a "very difficult" language to grasp, much less master. One would be better off learning the fundamentals of programming through Python or JavaScript first before diving deep into the world of systems-level programming.
Rich Ecosystem of Maturing Packages
Over the years, crates.io has accumulated over 50,000+ packages. Among the most beloved are rand
(for random number generation), syn
and quote
(for macro-related utilities), actix-web
and rocket
(for web server frameworks), piston
and bevy
(for game development), clap
(for parsing command-line arguments), and serde
(for data serialization and deserialization).
However, despite the richness of the ecosystem, I am still uncertain whether I can definitively say that it is fully "mature". Indeed, many of the packages are considered to be "production-ready". But, if we were to explore the list of the most downloaded Rust packages (as of writing), we would observe that many of them have not been stabilized to (at least) v1.0
.
With respect to semantic versioning, these packages are not yet considered to be "feature-complete" by their authors. One can argue, then, that a significant portion of the entire Rust package registry is not yet "feature-complete". Hence, I assert that Rust enjoys a rich ecosystem of maturing packages.
It is worth noting here that I am careful in choosing the word "maturing" over "mature". In most cases, "production-ready" is sufficient, but if Rust intends to accumulate massive market adoption, achieving maturity and stability is a key milestone. This is exactly what makes C/C++ an "immortal language" of sorts. From media encoders to cryptography tools, the world quite literally runs on C/C++.
To the community's credit, the 2020 Rust Survey paints a more optimistic picture in regard to library support. According to 65.9% of the respondents, there have been "at least some improvement" in the library ecosystem overall. However, GUI programming appears to be the exception, noting that an overwhelming 73.1% of the respondents opined that the ecosystem lacked enough support in this particular application domain.
Conclusion
With all things considered, I would say that Rust truly lives up to the hype. Just like for any other language, if one were to invest enough time and patience, they would find that Rust is not so bad. After getting over the initial learning hump, everything else becomes much easier.
The high-level syntax and the zero-cost abstractions are a dynamic duo that makes systems-level programming an absolute breeze. In times where it is otherwise, the excellent language documentation is always there to lift us up. Meanwhile, the Cargo package manager makes dependency management an empowering experience. One only needs to pull in a package to extend low-level functionality.
On another note, adopting the Rustacean Mindset rewires the brain to think more carefully about certain software design patterns. The ownership model invites us to question the validity of our data structures. Although fighting the compiler will be an awfully frustrating experience, let us never forget that this is always done in our best interests. When in doubt, we need to step back, breathe, and think. A well-thought solution is always better than "spaghetti code".
Ironically enough, although a significant portion of the Rust package ecosystem is not yet "feature-complete", this gives me all the more reason to place my bets in the ecosystem. Considering the current capabilities of Rust despite the "limited" ecosystem, the future seems bright for the language.
Perhaps a decade from now, we will finally be able to crown Rust as an "immortal language" alongside the likes of C/C++. Personally, I am looking forward to that day! But until then, I must say that the Rust hype is indeed justified.
Before I conclude this article, I would like to cordially thank the Rust team for their excellent work on both the language and the community. Without them, we would have none of the innovations we enjoy today in systems-level programming.
-
To cite one reason, a linked list is considered to be a "last-resort" data structure in Rust primarily due to memory fragmentation and frequent heap allocations. In most cases, arrays and vectors are sufficient. ↩
-
Funny story: the "Eureka moment" occurred to me just before I was about to sleep at night. Yes, I was frustratingly restless after that point... 🤦♂️ ↩
-
This is done through the use of destructors that run when objects go "out of scope". In C++, this is analogous to the Resource Acquisition is Initialization (RAII) principle. ↩
Top comments (35)
One of things I like about rust is how well integrated the cargo ecosystem is with the rust environment. Along with what you said about docs and packages, I really like how well the testing is integrated. Personally I have found that it has encouraged me to write more tests and hence a bit "better" code. And again that had allowed me to refactor and change things without worrying too much, as failing tests would show what has broken, which I think leads to your point that it makes one write "better" code.
Don't forget
cargo fmt
. Having a opinionated style guide is really a blessing.While I do not know Rust, and honestly have not used it, the integration of cargo seems to be something I see that pops up a lot. I watched a video actually of setting up Rust and the basics of using it and immediately thought “this is what a development environment is supposed to be like. None of the whole let’s glue 20 different packages together in an environment and hope they work together”
Some languages could really learn a lot from that, but refuse to (I’m looking at you JavaScript).
Truly! It still surprises me how
cargo new
just works. The Rust Team has done an incredible job in making sure the developer experience is smooth.I totally agree! The seamless Cargo integration really makes the developer experience of the Rust workflow a thing of beauty.
Let me play a devil's advocate and list things which I think are worth to mention when it comes to pain points in rust and are sadly not mentioned in your post:
For example I can't really rationally justify using rust for my hobby data driven gamedev experiments, because the return on investment is simply too low. I use C# because it allows me to go low level enough (even to SIMD) while having garbage collector and bunch of TODOs with hacky code simply because I don't have enough time.
Btw you can end up with spaghetti code even in Rust, there is nothing which would stop you in writing long and messy block of code with terrible cohesion between modules.
You brought up valid points. To that, I don't have much to counter against because Rust truly suffers from relatively slower compile times and inferior tooling compared to the C++ equivalent (as of writing).
However, these pain points do not outright dismiss Rust's value in the ecosystem. From a developer experience standpoint, Rust empowers me to achieve low-level capabilities using familiar high-level syntax. Meanwhile, Cargo itself automates project management (from dependencies to testing).
All of these features (and the others I've mentioned in my article) combined truly make Rust a language worth considering, even despite its yet-to-mature ecosystem. Or at the very least, all languages should look to Rust as a prime example of a language that puts developer experience at the forefront of its values.
My experience with C++ had none of these "quality-of-life" features. One can argue that C++ is for the "hardcore programmers", but speaking once again from a developer experience standpoint, these languages do not feel empowering. In fact, they are quite intimidating, to be honest. Perhaps that reflects more on me than the language; my point is: if a language goes out of its way to accomodate for developer experience regardless of expertise, then I am all for it, even if the ecosystem will take some time to mature.
Now, regarding your argument on C#, I don't have much to say, but I'm glad C# empowers you in that way! At the end of the day, what really matters is how a language empowers us to create and innovate in the technological space.
For me, I find this in Rust. For you, this is in C#. For others, it may be in C++. For some, it may be in Python and JavaScript. Regardless of language, developer experience and empowerment should always be the basis of our judgements.
With that said, thank you for your insights on the pain points. I must admit that I have failed to add more of them in the article. I feared that they may further lengthen an already lengthy article, hence the rather skewed commentary. But that's what the discussion threads are for, right? Thank you for adding value to the article.
Rust is gaining a lot of traction in security-critical low-level software like browser engines, etc. IMO that's where it shine. You can't write a CSS parser, HTML parser, JS interpreter, etc. in C#. You can do it is C++, in fact most are written in C++ today. However, security bugs are starting to become even more of a problem as the world gets more connected. Which is where Rust come in. You can access all the low-level stuff you can from C++ and get the same predictability and performance, but you get language-level protection against memory errors causing security vulnerabilities.
Well, technically, you can still write a parser and an interpreter in C#. It's just that the language may not be the most ideal option.
And, also, Rust isn't exactly bulletproof from all security vulnerabilities. The onus is still on us, the developers, to make sure that nasty bugs (like buffer overflow vulnerabilities) are avoided. But sure enough, Rust does help in avoiding many memory issues and pitfalls.
You can write it in C#, sure, but not with the performance and memory footprint required. Browsers are already notorious for their appetite for RAM. That's why it is traditionally done in C++. And it is orders of magnitude easier to avoid memory bugs causing security vulnerabilities in Rust compared to C++.
The JavaScript ecosystem is notorious for the todo list made with various approaches within the language... Todo list makes sense as a familiar UI project with different states and the right demo of complexity.... Is there a good equivalent Rust project? Something that demonstrates why Rust or why this approach within Rust?
Iced is a fantastic Rust GUI project that compiles to desktop native and WASM targets, using low-level GPU APIs. It has a good todo-list example that can serve as an example of what a Rust GUI project looks like.
github.com/hecrj/iced/tree/master/...
I've been using Iced now for a personal project, and it's actually really good. Still learning some ins and outs, though.
Yes, there is! As far as I know, the Rust equivalent for the JavaScript todo app is the humble CLI app. There are no particular requirements for the app since it would be up to the learner to make it their "crash course" around Rust's features. As the learner continues to design their app around the ownership model, it gradually becomes apparent how powerful it can be when scaling across multiple threads.
After that point, who knows what the future holds? Maybe the next step is to build a multi-threaded web server!
Nice read (for what I've read, I skimmed, coming back later)! I've written a couple of handfuls of mostly cross-platform CLIs in Rust in the last 6 months or so and I'm loving it. I don't do any web stuff (other than CURL), but otherwise my bins are lighting fast, comparable to Fortran (the only comparable language I'm fluent in, I know lots of languages intermediately) in some area for computation. Been playing with the PBR crate for my next little project.
Of course, I'm still green, so I don't have any deeper opinions of it yet. Oh, I do like closures.
This is my latest project if you're interested (please remember I'm still a NOOB lol):
BlackHosts
Thanks for the read!
Ian, what crates do you leverage when building CLI's? Also do you wrap the Rust code into a NPM package or something that is more portable or just stick to the raw binary compiled for various platforms? I'm brand new to Rust and thought maybe I'd try a CLI project to get started.
I just stick to raw binary and sometimes I'll release a .deb for Debian distros (and maybe an AppImage if needed for portable dependencies), but I code for myself, family, and friends so I don't do anything more extensive for releases. I'm not sure what you mean by "leverage", I try to stay minimal with external crates and just use whatever is needed for my small projects. I use the regex and recolored crates a bit and the rest all depends on the project.
Dang I loved this. I've been hearing all about Rust and memory safety, but this is the first post I've read that really digs into the qualities that are helping it gain so much traction. Thank you for writing this post!
I came from Frontend background, I have used Java before (never liked it, or could not understand how something gets available magically).
But I love Rust now. I'm comfortable with Actix web for API and Bevy for game development. And all this as a hobby, I dont see myself every leaving Rust the way I left GO so quickly. github.com/steelx
Great summary, thanks! I think my biggest beef so far with rust is that I'm really used to easy dictionary usage (literals, operators) from languages like Perl, JavaScript, Kotlin, and Python. HashMap just isn't quite the same.
It feels to me that if you first learn something like C# you won't ever want to touch C++
Great article!
There is a trend towards writing more immutable, functionally pure code and I’ve seen the benefits of this. Does this resonate in any way with you or in how rust is designed?
Yes. Variables and references are immutable by default, and have to be explicitly annotated to be mutable. Also, many features will remind you of functional languages from the ML-family (especially Haskell): algebraic data types, monadic exception handling, typeclasses, associated types.
Yes, it does! But not in the pedantic sense, of course. I still see the value in traditional object-oriented code. Rust is just there to nudge me in the right direction when I attempt to get too clever for my own good.
Overall, what resonates with me the most is the fact that Rust effectively synergizes functional-style code and object-oriented code. It forms a "yin and yang" of sorts between immutability and mutability. This is unlike many languages where the paradigms are set in stone. For instance, Java is
notoriouslyobject-oriented while Haskell isconfusinglyfunctional. Rust just seems to strike a good—if not perfect—balance, you know?Thanks for the article. Would you mind to describe some web (backend) use cases?
Sure! Since Rust can be thought of as a general-purpose programming language, the use cases are quite broad. You can, for example, use the Rocket framework and the Actix Web framework to build web servers. Many people shift to a Rust back-end to reap in the performance benefits, which is why you will most likely find Rust in front-facing API infrastructures. REST APIs, in particular, are blazingly fast thanks to the serde crate's quick data serialization and deserialization.
The Yew framework has also been gaining traction for using Rust in the front-end—albeit indirectly through WebAssembly.
There are also use cases for web development tooling. The swc JavaScript/TypeScript compiler has become quite popular thanks to the performance gains from Rust.
Besides the typical web development use cases, Rust can also be used for image manipulation. A popular package for this is the image-rs crate. If your back-end requires some kind of image generation, manipulation, and compression, then Rust has your back on that one, too! Combined with a REST API, the possibilities are endless.
I hope it is clear that the use cases are broad. Rust is not a domain-specific language, so if you can think of a use case, then it is most likely feasible with Rust. Much of this can be attributed to the wealth of packages in crates.io. I suggest exploring the registry for inspiration. The possibilities are truly endless.
Grateful!