A little example. Yesterday I was making changes to our C++ client library and I wanted to improve the example in our documentation.
We use a dedicated protocol called ILP for streaming data ingestion and each of the inserted rows has a designated timestamp.
In the Rust example, I using added support for chrono::DateTime and it was trivially easy for me to add a timestamp for a specific example date and time: Utc.with_ymd_and_hms(1997, 7, 4, 4, 56, 55).
Our C++ library instead takes an std::chrono::time_point. I wanted to use the same datetime. As far as I can tell it requires first going through the old C "struct tm" type (which is local and not UTC), then converting to "time_t" then converting to utc via gmtime and then constructing a time_point from that. After 10 minutes the code got too long and complicated so I just substituted it a timestamp specified as an int64_t in nanoseconds.
Don't get me wrong, the C++ time_point is a work of art in how flexible it is, but unnecessarily complicated in most cases.
I should add that I also spent 45 minutes yesterday debugging a CMake issue.
Rust is not easy to learn, but it's just more modern and productive.
C++ is still great if you've got a massive team, but at our scale I don't think it makes any sense.
Java's mistake is that they went much too far when they nerfed the language. For highly skilled developers that could write robust C or C++, the poor expressiveness of Java made many easy things difficult or impossible. It was clearly a language designed with business logic in mind, any systems-y software was an afterthought. The release of C++11 ushered in the era of "modern" C++, killing Java's momentum in the systems-y software space.
Rust, in my view, attempts to solve the same abstract problem as Java -- we will never produce enough developers that are competent at writing C or C++. Rust talks about "safety by design" in the same way Java did when it was first released. However, it does so without being so limited that highly skilled software engineers will find the language unusable or giving up so much runtime performance that the operational economics are poor. In my mental taxonomy, Rust is pretty close to what Java intended to be but then never quite delivered on.
My naïve guess is high performance data structures with reference cycles?
And indeed C++ templates are a lot more like Rust macros than Rust generics: They're turing-complete.
Joint with some interesting language choices like SFINAE (substitution failure is not an error), you end up with the ability to specialize functions, methods and whole classes in C++.
You can also have functions that return different types.
C++ templates work like duck-typing within a static language: In Rust you need to say what traits your generics need to support. In C++ it will try and substitute and if it fails (say, because T doesn't support the required methods) it will try another substitution until none are left.
If none of the substitutions work, you will be shown ALL of them in error reporting: This is what leads to pages and pages of compile errors of single-character typos in C++.
Templates are really cool, but also pretty confusing when reading code since you're in a guessing game of what types will fit the constraints imposed by the _implementation_ of the function.
From C++20 there's concepts to make templates work a little smoother.
There's been whole books written about how to abuse templates: these are pre-requisite knowledge when working in large codebases.
Another one, with a more limited audience, is data models where object ownership and lifetimes are inherently indeterminate at compile-time. Since C++ allows you to design your own safety models, since they are opt-in and not built into the compiler, you can provide traditional ownership semantics (e.g. the equivalent of std::unique_ptr) without exposing the mechanics of how ownership or lifetimes are resolved at runtime. Metaprogramming plays a significant role in making this transparent.
Those are the two the matter the most for my purposes. They save an enormous amount of code and bugs. Rust has a litany of other gaps (lack of proper thread local, placement new, et al) but I don't run into those cases routinely.
The data structure thing you mention would be annoying but to be honest I rarely design data structures like this. For performance, most data structures tend to rely on clever abuse of arrays.
I fell a lot more learning on snowboard than on skis.
Skis had a much quicker early learning curve, and made me feel over-confident. Several times I found myself on trails too steep for my skills, and the skis made me have to work hard to recover. Most of the techniques I learned as a beginner didn't work beyond green trails, and blues, blacks all required new, harder, techniques.
With snowboarding, because I fell a lot more early, my confidence slowly grew. Meanwhile the techniques I was learning on the greens, and the tool in general, that were HARD to learn on the greens were actually EASIER on the blues, and continued to work on the black-diamonds. Granted I also had to learn new techniques on the harder trails, but the beginner techniques and the slower development made me MUCH more confortable across the whole mountain much faster than skis.
Double-blacks are more like unsafe rust. :-D
Snowboarding is brutally hard to pick up at first, unless you hate your tailbone. I have never really tried, I'm too old for that kind of pain.
But I'm a telemark skier, so the worst of both worlds :-) Maybe telemark is like advanced C++. But probably more like Haskell.
I think once you get familiar with it, it is just slightly slower to write than python.
Are there some things faster to write in python? Sure. But I find the mental overhead is significantly less (for me) in Rust, and overall dev time is about equal, since I typically hit far fewer bugs and spend less time reading docs in Rust. I can't remember the last time I hit a footgun in Rust. Seems I hit one every week in python.
We recently migrated a ~10k SLOC Django JSON API server to axum/sqlx (Rust). I couldn't be happier - faster to ship new features, faster to refactor, fewer bugs, and response times got about 10x quicker.
Sometimes I do run across problems that are difficult to express in Rust without resorting to interior mutability, and it can slow me down to figure out the best way to model my data.
There are definitely aspects of Rust that are much more complex (typically with a tradeoff of more expressiveness, but not always), but at least in my experience, these are usually areas where you can't easily express the same thing in Python. I think many times people forget that you can frequently `clone` your way out of many issues if you are trying to move fast.
And of course areas where I still only use python: manipulating tabular data, making graphs, quick scripts for interacting with APIs etc.