> Personally, I think what C++ offers is the same as Rust.
I think this dismisses incidental complexity that can certainly exist in general, and certainly does exist in C++.
Just look at how complex move semantics are in C++:
* you have & and && references,
* there are all sorts of categories (e.g. glvalues),
* in a template && means something different (but not always),
* std::move by itself doesn't actually do anything (yes I know it's a cast but this is still confusing at least upfront),
* even if you do pass the result of std::move (or otherwisie know you have an rvalue reference) to a constructor then it's still possible that the object will actually be copied with no error or even warning.
In Rust, the difference is enormous:
* When you move from a value, it's guaranteed that the old object's destructor will not be called, so you don't need a move constructor that hackily sets up an empty value so is destructor won't do anything;
* Move is always a bitwise copy so it's easier for the caller to understand and free for the implementor to implement (this is enabled by the previous point)
* Move is the default instead of copy which makes a million times more sense because you can just implement copy as a regular method that happens to return (move!) its result (whereas you could never implement move as something that returns its result by copying) and if you actually want to copy a value to a function you can just call the copy method and move that value to it.
This final one is key is really, it's not possible because of C++'s history and backwards-compatibility requirements and that's why it had to come up with the crazy system it now has. Rust has many nice features but really the move vs copy stuff by itself is a huge saving in complexity, and the fact that Rust manages it at all proves that it is not intrinsic to the problem.
(Disclaimer: I have worked with C++ on and off full time for more than 15 years, whereas I have only tinkered with Rust.)