I restarted learning Rust again just a week ago, so perhaps this isn't a fair comparison.
I have a pretty strong background in C++ too, so arcane syntax and many of the principles for system programming are familiar.
But... I've been using Google's Comprehensive Rust course. It's a good introduction, and the exercises are pretty good, though I've had to do extra research to complete all of them.
For the extra research I've used "Programming Rust" and Google/Stack Overflow to fill in the blanks. My C++ background has helped to know what to search for likely.
It's divided into 4 days of morning and afternoon sessions, but the last day is Rust on Android, and can be safely skipped if you're not doing that platform.
I have programmed a few languages over the years. Most of my C/C++ dev was pre-STL days. Lately, work has focused more on Kotlin.
I had heard about Rust, but it wasn't until I saw a video from Let's Get Rusty in Feb that I started trying it.
I started with:
Since then I have also been using:
I am currently waiting for:
In general there are a few things that I find I really like, and a few things that are a bit more challenging.
Cargo is awesome. I love it. Well, mostly. More on that later.
I am only developing on Linux, so can't really speak to Mac or Windows.
I am using IntelliJ instead of VSCode. After 18 years of using it, I'm comfortable with it. Using clippy/rustfmt/cargo in it works well.
Most of the language is easy to grok. With my background in Java and Kotlin, I found traits and generics easy. With my background in C/C++, I thought the pointers were fine, though I thought I had gotten away from them years ago. The Deref trait reminded me of the magic when C++ first came out, TBH.
So what problems have I run into?
The first is that when getting a brand new book, the libraries are often so far out of date that the mechanics are no longer applicable to newer versions of the library. While you can pin to the older library version when going through the book examples, you are not learning what you will be doing moving forward.
The second has to do with the cargo error reporting. New users will rely heavily on it to help them self-educate on what they are doing wrong. Usually that works well. Sometimes though, cargo tells the user to do something they are already doing and that can lead to confusion and frustration. ( Unfortunately, I just registered on hashnode to post this, so can't include a link. There is an example on my blog titled "error[E0716]: temporary value dropped while borrowed")
The last one that I think takes some effort is the lifetimes. I often get caught in a loop of being told to add them, then told to elide them, then repeat.
I started a couple of years ago and ran into the classic learning wall that others hit as well. I think a major issue, personally, was that I had very little CS background, or experience with serious programming. My previous programming experience had been small programs written largely by example, e.g arduino style C++ or tiny python scripts, and I understood very little about what was going on.
Rust was a slap in the face, but also extremely helpful. It's a language that generally doesn't "tolerate" a surface-level understanding of how to write it, but instead demands a deeper understanding of exactly what you're doing. This is probably why it's so difficult to learn - you can ignore hundreds and hundreds of parts of C++, but you need to learn them in Rust.
I also struggled with learning the idiomatic patterns and higher-level behaviors of the language. Some patterns in code, like iterators that mutate things or self-referential structs, tend to quickly balloon in complexity. Even if I understood the examples in the book, dealing with the incredibly complexity when you walk into these traps is tremendously difficult.
Nonetheless, I find Rust to be one of the easiest languages to truly learn. It's very easy to get started in Python or even C/C++, but to master them is extremely difficult. Rust has a clear, well-written, readable reference. It's got a book that walks you through the most difficult concepts. There's also a benefit in that its search results are far less noisy - "how to do X in Rust" returns much more useful results than "how to do X in C++", which typically returns poorly written code (and articles that GPT could do a better job with.)
Every time I start a project in Rust, I face the same hurdle -- it is really hard to sketch out code that doesn't work as you develop things. The compiler is (properly) so finicky about correctness, I find it very taxing to build things I have not figured out the final impl for. In Java, C#, Python, Ruby, you can have lots of things dangling and not working and still compile and run other parts as you code.
In Rust -- well, this is the other side of how good the compiler and language are regarding correctness. But it translates into immense friction for me.
The borrow checker is not a big problem; I spent years in C. But the myriad of ways to do things and lack of clear tradeoffs between them is annoying. The only way to learn is to write a ton of Rust code.
The problem then becomes you can't code easily in Rust in the background and in some other language in your day job. You need immersion, as it stands.
I found learning basic rust fairly straight forward; the biggest time drain was and still is finding crates and figuring out how to use them. I find that many of them have rudimentary documentation and many of the example code snippets are too trivial to gain understanding of the more complex use cases.
I've really been loving Rust as I've learned more over the past few months. Your point about it lacking a well defined use-case is something I've struggled with. I want to create useful solutions to real problems, and I struggle to find the instances where another language wouldn't be better in its current state.
I'm in the process of learning Rust, and my biggest hurdle so far has been that the terminology doesn't seem to match the intent. In my opinion (and a few others too, there are various discussions and blog posts about the design decisions) 'mutable' and 'immutable' would be better understood as 'exclusive' and 'shared'. Exclusive access is a requirement for mutability, yet we express that a variable is mutable, not that it we have exclusive ownership. We have Cell<T> and RefCell<T> which seem to be exempt from the rules of mutability, and it's hard to understand why Rust has such rules when it allows them to be broken. Most tutorials give examples of how to use these types, but it took me a long time to understand how the restrictions on these types make it safe to break the rules
My professional experience with Rust was to build REST and gRPC microservices for a mobile app and its browser admin for over a year. I also contributed to some crate on Github, if only modestly.
What I found the most challenging to learn wasn't really the concept of ownership and borrowing in itself, but more how the whole type system and ecosystem revolves around it. Also a simple thing like what can be done and what shouldn't even be attempted (in the usage of traits and the language in general).
That's actually something I've been trying to write an article about for weeks but still struggle to narrow it down to a "cognitive-friendly" material (I mean, something that doesn't require the reader many hours to read and many days to digest).
Simply because explaining Rust on the surface will still leave a beginner confused as soon as (s)he's digging deeper, but explaining it in details require tremendous amount of explanations and examples.
As much as I love Rust (and wouldn't see myself coding in another language anymore, if not for a living), there's a couple of criticisms I'd like to share :
most of its learning resources are still far too academic for an average person (which contrasts with its empowering everyone aspiration).
➡️ as a mere example, variance, covariance, contravariance and their related explanations are completely appropriate and accurate, but somehow I feel like they could be explained in way simpler terms in general. I think Rust still greatly suffer from a lack of scientific popularization.
part of learning Rust past the basics is actually mastering its (numerous) APIs.
➡️ for example, to be able to return an Option<&T> from a method with an Option<T>, one has to know about as_ref() method. Simply missing it out from the docs or compiler's hints mean that somebody could be mislead in thinking that it's simply not possible at all. Luckily, the community greatly helps here 🥰.
➡️ another example where I think (almost) everyone can relate : making reusable code as a beginner with Diesel can quickly drive you to the verge of insanity 😂.
it's preferable to try an implementation on a bare minimum crate, but one usually has to learn it the hard way as it's not stated clearly enough IMHO.
➡️ for example, when refactoring my code the first time(s) : this is something I'm pretty confident with since I did it countless times with other languages, but with Rust in the beginning I simply lost hours tackling things here and there to please the compiler on each and every nitty-gritty details, almost losing track of what I was doing, but persevering since I thought my idea was correct... just to realize near the end that the compiler would not accept it implemented this way, as it wasn't deemed correct or safe (sometimes also turned out that my attempt was indeed correct, but I was missing the last part of the puzzle, whether it be the use of a certain method in a specific place, or knowing how to deal with some crate's method lifetime). Of course this is desired, and perfectly understandable since the compiler has no way to know what you're trying to achieve, but I wish I was warned right from the beginning not to attempt to do so on large codebase and instead first experiment for feasibility on tiny sample codebase, in order to save on time and frustration. There's no one or nothing to blame here, mistakes were mine, but would it hurt to state it loud and clear in the Rust book ?
These being said, I don't want you to think that this comment is a rant either, simply because Rust compiler already does an astonishing job at assisting the developer on its journey, and its current limitation are not only perfectly understandable, but they keep being pushed over time. Also, as the language becomes more widespread, it will as a matter of fact become more beginner-friendly in terms of learning resources.
Basically, I have reached a similar conclusion a few month ago and started Rust Academy to lower the learning curve by writing articles on topics known to be tricky (memory, borrow checker) and uploading a bunch of video tutorials on youtube. However, I think there is a certain misconception around Rust, that is, the steep learning curve would be problem.
That is often not the case. Senior engineers who are familiar with MISRA and SAST simply love Rust because they can safely skip about 80% of MISRA because it's basically baked into Rust. They just get the handbook, read it, and write code. No need to lower the barrier.
Midlevel engineers who have written at least 4 to 5 different languages may bump into the borrow checker, but once understood these guys are productive.
The only people I have encountered who really struggle with Rust are your novice coders without much CS background. They may have written JS or Python, maybe some Java, but for them understanding the Stack, Heap and how memory abstraction works really is the first obstacle before Rust is an issue.
At Rust Academy, I spent a great deal explaining memory better and so far feedback was quite positive although there is certainly more to do.
My first Rust project was writing IPv4 mesh networking for LoRa sticks crockpotveggies/lora-mesh-rs
To grasp the language I brute forced the compiler until I could more easily grasp the concepts. I think there might be something unintuitive with the language itself when making references and “borrowing”. Perhaps the symbology could be more clear as to what it means to do X. I also found it difficult to find learning resources that could give me an adequate introduction…albeit this was two years ago.
Once I understood the language I fell in love with it and its memory safety. However, the ecosystem still feels a bit premature and perhaps with some language tweaks and polish on the tooling it could be just as widely adopted as Go or C++.
If you were to ask me to choose between C++ and Rust for a project, I would start with rust.
I started learning it it last year. Here's my approach:
Read and took my time understanding the first eight or nine chapters of The Book. This took me around two weeks
I thought that gave me good enough foundation to begin a project so I started building one
I worked on the project for about six months. During that time, I revisited The Book multiple times to learn about different parts of Rust as I needed them. I also generously employed other resources like blogs and studying code from other Rust projects.
I'd say it took me about two months to feel productive working with it but way longer to feel confident about the code that I was writing. Actually no, I'm still not confident that my code looks anything like idiomatic Rust. Anyhow, I love Rust and it's now usually my first choice when considering a personal project.
TL;DR: Building projects and picking up things along the way as the project grows in complexity works best for me.