My impressions of Rust after a year of working with it
A year+ of working with the Rust programming language for embedded and web development
👋 Are you looking for a community to join other Rustaceans where you can learn Rust better, or from scratch? This is exactly what the Rust Never Sleeps community is for! Come join us in our Slack group and GitHub projects space.
Simply complete this very short intro questionnaire and we’ll be happy to welcome you into our community of fellow Rustaceans.
The rise of Rust
A little more a year ago I began learning and building software with Rust. It was a language that I'd heard the name of, but otherwise had no previous experience with. For a while I brushed it off as a niche language that would remain so and thus not worth my attention nor my time. I’ve rarely been happier to be completely mistaken. I began to notice Rust consistently following an adoption curve similar to other successful open source projects, the most famous example being the Linux kernel.
Over the last year, Rust continued to garner significant new interest from companies and developers. Polling 70k+ developer from around the world, Stack Overflow's annual developer survey shows Rust growing in adoption, most notably being the most desirable language to work in and the most loved. Rust has held this exceptional distinction for 7 years in a row. It also grew in developer marketshare from 7% of survey respondents in 2021 to over 9% in 2022, an impressive 22% increase!
In 2022, Rust also became the second officially supported language for developing Linux kernel modules and now being only the only alternative for developing core parts of the kernel itself other than with C. This is quite a significant milestone for Rust adoption for at least two reasons:
First, Linus Torvalds, father of the Linux kernel and long time project lead, is not shy about criticizing or rejecting technologies that he finds to be awful. I don't mean to imply that he isn't critical of Rust, but he finds Rust and the features it brings compelling enough to help make the kernel better. Linus recognizes that Rust brings incredible correctness and safety in a way that C does not. This difference alone is significant enough to adopt Rust for kernel development. This is a major decision for one of the world’s most significant and critical software projects - introducing a new language to a large software project is one of the most risky and complicated changes one can make.
Second, adopting Rust in the development of the kernel means increased adoption of the language by industry kernel module developers who write device drivers for their company’s products. Rust shines as a systems language first and foremost, and by including support in the kernel, the job market for Rust developers to work on the Linux kernel and device drivers increases and provides a strong alternative to C. I’m convinced that Rust will become a more respected and a more entrenched mainstream language because of this.
How I got introduced to Rust
What got me interested in developing software with Rust in the first place?
I've worked in C and C++ professionally and non-professionally since I was in high school. Both are performant and expressive languages that have excited and frustrated me for years. And I've written some incredible embedded and systems software using these languages. Working with them has always felt like a love/hate relationship, like I was waiting for something better to come along and give me the best of these languages while addressing the frustrating parts.
As I started working with more modern languages like Elixir and Ruby, I realized how much I was missing out on in language innovations by focusing on C/C++. These modern languages bring immense productivity gains to developers through new and refined language constructs and patterns, and modern build tooling. Setting up a new C/C++ project is a real pain. The complexity of memory management and the pain of debugging run time memory issues is frustrating, error-prone and costly. With Elixir and Ruby, none of these frustrations exist. And as I've come to realize, the same is true for Rust.
I first had my interest piqued by Rust when a colleague mentioned that he was experimenting with building web backends in Rust. What he described is a language that contains the most attractive pieces of C/C++. But unlike these languages, Rust brings a significantly more modern approach to language constructs, patterns and tooling. This includes things like stricter memory management with no runtime garbage collection, zero-cost abstractions providing anxiety-free use of the entire language for embedded applications, and a strongly-typed language that takes this concept much further than C/C++. Because of this compile time strictness, Rust eliminates ~80% of the kinds of memory bugs and exploits that sneak into C/C++ programs, especially bare metal embedded ones.
If Rust delivered on these promises while being fun to develop software with, I knew I’d be excited to work with it for many years.
Now being over a year into working with Rust and having written a Raspberry Pi Pico to ESP32 WiFi crate (and others), has Rust delivered what it promised me? And what has it been like to work with Rust?
Has Rust delivered what it promised?
Yes, Rust has delivered everything it promised me, but let me list how it has done this:
Rust produces incredibly performant software that is comparable to C/C++.
Rust provides fine-grained control over memory allocation comparable to C/C++ eliminating fear of memory manipulation using strict mechanisms - strong type size enforcement at compile time, strong use of iterators, and a robust scoping mechanism for shared references.
Use of the entire language (except for the parts of std that need heap allocation) without fear of having a bloated binary or memory usage in embedded applications.
Ability to write modern-looking code that's easier to maintain, more concise, and more readable. A developer coming from Elixir or Ruby would feel right at home Rust’s pattern matching and other advanced execution control features.
Using Cargo, new Rust projects are a breeze to set up as is managing application dependencies.
Using the same CLI, Cargo also makes it easy to run and debug web applications, system utilities and even embedded applications on a device.
Rust provides the excitement I hoped for and it continues to motivate me to push into deeper mastery. I can't say the same about the other modern languages I've experimented with (except for Elixir).
What’s it been like to work with Rust?
Working with Rust continues to be an incredible journey, one that I'm continuing to invest significant time in. While my journey has been incredibly exciting, it's not been painless working with Rust. For example, someone new to Rust shouldn't expect their learning journey to be simple. But it continues to reward me primarily through requiring me to become a more modern developer by the need to conform to baked-in patterns and constructs. And the ecosystem/community also contribute to this by having and encouraging strong conventions of what makes for idiomatic Rust code.
However, please understand that learning Rust is not for the faint-of-heart. It's very challenging at times, especially during your first ~6 months, but also in ways similar to C++. It's a more consistent and well thought out language than C++. Working in a language that tries to provide this level of correctness, speed and control over memory is bound (Rust joke!) to be hard. My advice then is this: ask yourself if you have the energy to persist and devote at least a year to building a real software project with Rust. If you hesitate to give an emphatic “yes!”, then perhaps it’s not the right time to begin learning it.
Early days
As I mentioned earlier, when I first began to learn Rust, I noticed how much it seemed familiar to both C/C++ and Elixir. C, C++ and Elixir initially were the lenses through which learned and understood Rust. I was happy to see things familiar from Elixir, a modern functional language I learned during the first year of the Covid-19 pandemic. Elixir taught me a lot about modern programming through working with its functional constructs, and so I was very pleased to see that I could apply similar modern constructs with Rust.
I first approached writing Rust in much the same way to how I'd write C, by creating verbose and linear functions. A helpful gift I gave myself was permission to be a beginner again and write messy (and seemingly embarrassing) code. Similar to learning to write and speak a new human language, it takes significant time to become fluent in a new computer language even with having fluency of others. Here's an example application that shows my beginner-level Rust code - it's a single file, proof-of-concept library that brings basic WiFi functionality to the Raspberry Pi Pico. I look back on this code and smile - I’ve come a long way on my journey. Functionally, this project gets the job done and is technically Rust code, but it's no where near maintainable, idiomatic Rust.
I encourage you to approach learning Rust the same way - allow yourself to write a lot of messy code and any need for immediate mastery
Some early things I noticed: Rust traits seem to be like purely abstract C++ classes, yet it's now clear that they are not the same thing. I'm used to writing embedded applications using simple structs and functions and some use of C++ classes, so I began by writing Rust this way too. I wrote simple structs and used existing traits instead of defining my own.
I also noticed many familiar constructs similar to the ones used in writing performant C/C++ code. For example, Rust has everything that C/C++ has for memory manipulation, but reimagined with safety and performance in mind. This provided me with the confidence and the motivation to keep learning Rust further while also no longer feeling any need to develop new projects in C/C++. It truly is ready to take over the role that C/C++ have provided for decades.
One area that excited me about working in Elixir was its nature as a purely functional language. This proved to be productive and exciting enough that I hoped to develop in a similar manner with Rust. When I first started learning Rust, it wasn't immediately obvious whether Rust could be considered a functional language. Rust has functional characteristics to it, but it isn't a functional language. To illustrate this point, early on, I used Rust's support for pattern matching. I now regularly use Combinators in a Builder pattern which is similar to using Elixir's pipe operator. This provides much of what makes Elixir so productive and fun to write software with.
The long journey
How have my experience and perception of the language changed over the past year? It's a good question to examine because it's important to know when to apply a complex "systems" language like Rust to a problem you're trying to solve. This is something senior-level engineers need to be able to evaluate with strong confidence in their decision.
As I said earlier, Rust is not an easy language to learn. As a rough analogy, if you've ever played the ancient (and fun!) game of Go, learning Rust is a lot like learning to play Go. At first it seems straightforward to solve reasonable problems with Rust. But like Go, Rust is challenging to master the details. Even still, I strongly feel that Rust is incredibly rewarding to learn and adopt as one or more primary languages. Why?
At a high level, as I mentioned above, Rust will also make you a much better software developer, and I think it does this better than any other language I've developed with. Let's unpack this in the context of my own journey to learn Rust.
Learning any computer language begins with the basics: what are the primitive types, how does execution control flow work, what are the basic ways of grouping like data into structures, how does one display text to a stdout/stderr console, how does one loop/iterate over collections, and how does basic error handling work?
Rust does all these things well which both encourages and forces you to also do these things well. Combined with the ease Cargo brings to managing dependencies and building your project, making software that can solve simple to intermediate level problems is pretty easy with Rust. Like I mentioned above, I was able to make a C++-looking proof-of-concept application without much trouble.
But as I worked with my Rust Never Sleeps community to turn this PoC into a maintainable open source project, both Rust's full power and learning difficulty became more visible.
For example, Rust uses traits to describe software interfaces. As I mentioned previously, I thought that traits were roughly the same thing as abstract classes, but in practice, this isn't quite right. In some ways traits are similar to abstract classes and also like virtual classes, but Rust has no formal concept of object-orientation. The nuance has taken me a lot of study and reflection to understand and harness the differences.
Working with traits has been hard. I find a need to revisit how to use traits quite often, especially when using generic types.
Rust forces me to stop learning by analogy to other languages I already know - this is hard to do at first. My approach to learning Rust now consists of studying mature Rust examples, referring often to the Rust Book which describes core language concepts, and practicing fuzzy concepts often by implementing many naive implementations. As I've practiced Rust by staying focused on 1-2 Rust projects at one time, I've naturally pressed deeper into the more advanced concepts of Rust. Armed with this deeper level of knowledge and experience, I'm able to return to my existing naive code to simplify it by applying increasingly more mature Rust patterns with greater ease straight from my mind.
Rust rewards me with correct behavior at runtime. I've never experienced such helpful upfront correctness of execution quite like I do with Rust - especially when developing for embedded devices. I'm used to spending significant time debugging undefined behavioral bugs, but this happens so infrequently with my Rust code.
Where I do spend a lot of time is iterating with the Rust compiler and working out borrow checker issues, but I’m rewarded by robust runtime behavior. This alone makes Rust worth considering if it's worth your efforts to learn it. It also begs the question: is it time to leave C/C++ in the waste bin of history, no longer using it for your new projects?
Rust has become more straightforward to develop with the more I've stuck with it. This video captures this concept in a humorously accurate way. There's no other way to learn Rust than to spend significant time writing Rust code. Learning and applying it once a month won't allow anyone to move beyond being a beginner Rust developer and will leave you feeling quite frustrated.
Should I learn Rust?
Yes, if the following is true for you:
I desire to become a really great software developer in a modern language capable of productively developing any type of software system
I routinely need to develop software with compact memory use without being restricted to using a subset of the total language (e.g. like with C++)
I need to develop performant software, particularly on a single CPU core (but not limited to a single core)
I need to develop correct and memory-safe software without worry and prefer or require a runtime without a garbage collector
I've mastered another language and I'm looking for a new challenge
No (or maybe), if the following is true for you:
To date, I've never (or hardly) developed any software
I need to rapidly prototype a new concept to find product-market-fit on my own or at a startup company
I'm not willing or curious enough to persist when certain Rust concepts become challenging
I have no interest or need to learn how memory management works for the type of software I want to develop
Concluding thoughts
Ultimately, if you're unsure if Rust is a good language to learn, it can't hurt to give it a try. You can be productive writing significant software implementations in just a few months of frequent practice with Rust. To become very proficient, Rust demands that you keep working with it over a significant period of time (e.g. 1 year+). If you can't dedicate time to doing that right now in your life, I recommend learning a different language (here’s my plug for learning Elixir). You can always come back to learning Rust when your circumstances and energy change.
If you're interested in learning Rust and are excited to do this work now, I encourage you to start today. And if you want help along the way, that's part of what I do in my software engineer coaching. I encourage you to schedule some free time for us to discuss how I can help you. You don't have to struggle through learning Rust alone and I can help you become a better developer as I’ve helped many other engineers.
As always, thanks for reading and sharing this post with anyone that will benefit from reading it.
Until next time, please enjoy!
P.S. What’s your experience learning Rust been like? Feel free to share yours in the comments below.
P.P.S. If you’re looking to become a much more confident Rust developer, I invite you to message me. I’m a software engineering coach that would love to help you set up a plan to master Rust and help you become a more senior engineer.
When I betted on Rust other laughed at me. Today it pays off.
I'll be 70 in a few weeks. In my professional career, I was a C/C++/C# developer. I needed to develop a backend API for an iOS app last year and decided to use Rust because of all I'd read about it. Yes, the learning curve is steep. I hung in there like grim death until the tricky bits became clear. I love the language and the ecosystem. I am still very fond of C/C++/C#, however.