> Rust types can be very verbose, RefCell<Optional<Rc<Vec<Node>>>>
Anything in any language can be very verbose and confusing if you one-line it or obfuscate it or otherwise write it in a deliberately confusing manner. That's not a meaningful point imo. What you have to do is compare what idiomatic code looks like between the two languages.
C++ has dozens of pages of dense standardese to specify how to initialise an object, full with such text as
> Only (possibly cv-qualified) non-POD class types (or arrays thereof) with automatic storage duration were considered to be default-initialized when no initializer is used. Each direct non-variant non-static data member M of T has a default member initializer or, if M is of class type X (or array thereof), X is const-default-constructible,
if T is a union with at least one non-static data member, exactly one variant member has a default member initializer,
if T is not a union, for each anonymous union member with at least one non-static data member (if any), exactly one non-static data member has a default member initializer, and each potentially constructed base class of T is const-default-constructible.
For me, it's all about inherent complexity vs incidental complexity. The having to pay attention to lifetimes is just Rust making explicit the inherent complexity of managing values and pointers thereof while making sure there isn't concurrent mutation, values moving while pointers to them exist, and no data races. This is just tough in itself. The aforementioned C++ example is just the language being byzantine and giving you 10,000 footguns when you just want to initialise a class.
> > Only (possibly cv-qualified) non-POD class types (or arrays thereof) with automatic storage duration were considered to be default-initialized when no initializer is used. Each direct non-variant non-static data member M of T has a default member initializer or, if M is of class type X (or array thereof), X is const-default-constructible, if T is a union with at least one non-static data member, exactly one variant member has a default member initializer, if T is not a union, for each anonymous union member with at least one non-static data member (if any), exactly one non-static data member has a default member initializer, and each potentially constructed base class of T is const-default-constructible.
That's just a list of very simple rules for each kind of type. As a C++-phob person, C++ has a lot of footguns, but this isn't one of them.
This was a random snippet of complicated rules to illustrate what I mean. C++ has significant footguns and complexity when it comes to initialising an object. Even sticking by "Effective Modern C++" rules is a significant cognitive burden on something that should be simple and straightforward. Rust has (mostly) no such footguns; its complexity follows from the essential complexity of the problems at hand.
Rust prevents the footgun, but also prevents shooting in the direction where your foot would be even if it isn't there.
There are absolutely places where that is required and in Rust those situations become voodoo to write.
C++ be default has more complexity but has the same complexity regardless of domain.
Rust by default has much less complexity, but in obscure situations outside of the beaten path the complexity dramatically ramps up far above C++.
This is not an argument for or against either language, it's a compromise on language design, you can choose to dislike the compromise but that doesn't mean it was the wrong one, it just means you don't like it.
A relatively simple but complex example, I want variable X to be loaded into a registerer in thos function and only written to memory at the end of the function.
That is complex in C/C++ but you can look at decompilation and attempt to coerce the compiler into that.
In rust everything is so abstracted I wouldn't know where to begin looking to coerce the compiler into generating that machine code and might just decide to implement it in ASM, which defeats the point of using a high level language.
Granted you might go the FFMPEG route ans just choose to do that regardless but rust makes it much harder.
You don't always need that level of control but when you do it seems absurdly complex.
> I want variable X to be loaded into a registerer in thos function and only written to memory at the end of the function.
> That is complex in C/C++ but you can look at decompilation and attempt to coerce the compiler into that.
> In rust everything is so abstracted I wouldn't know where to begin looking
I don't know if I fully understand what you want to do, but (1) controlling register allocation is the realm of inline asm, be it in C, C++, or Rust. And (2) if "nudging" the compiler is what you want, then it's literally the same thing in Rust as in C++, it's a matter of inspecting the asm yourself or plonking your function onto godbolt.
I agree that you will probably just end up writing ASM but it was a trivial example, there are non-trivial examples involving jump tables and unrolling loops etc.
Effectively weird optimisations that rely on the virtual machine the compiler is building for vs reality, there's just more abstractions with rust than with C++ by the virtue of the safety mechanism, it's just plain not possible to have the one without the other.
The hardware can do legal things that rust cannot allow or can allow but you need to write extremely convoluted code, C/C++ is closer to the metal in that regard.
Don't get me wrong I am all for the right abstractions, it allows insane optimisations that humans couldn't dream of, but there is a flip side.
My high level understanding of the UB concept is, that it means false positives to the question "Is that a valid program?". Given that the philosophy of C is mostly "Do what the programmer wrote, no questions asked.", it leads to designing the language so, that the probability of false negatives goes to zero. This obviously means, that the number of false positives goes up.
Rust basically takes the opposite approach of making false positives go to zero, which makes the false negatives go up, which you need to work around with unsafe or type gymnastics.
The third approach is to make both false positives and negatives be zero, by restricting the set of programs, which is what non systems languages do.
> Anything in any language can be very verbose and confusing if you one-line it or obfuscate it or otherwise write it in a deliberately confusing manner.
That type was not intentionally obfuscated or complex, it is actually pretty common to see such things. YMMV.
Anything in any language can be very verbose and confusing if you one-line it or obfuscate it or otherwise write it in a deliberately confusing manner. That's not a meaningful point imo. What you have to do is compare what idiomatic code looks like between the two languages.
C++ has dozens of pages of dense standardese to specify how to initialise an object, full with such text as
> Only (possibly cv-qualified) non-POD class types (or arrays thereof) with automatic storage duration were considered to be default-initialized when no initializer is used. Each direct non-variant non-static data member M of T has a default member initializer or, if M is of class type X (or array thereof), X is const-default-constructible, if T is a union with at least one non-static data member, exactly one variant member has a default member initializer, if T is not a union, for each anonymous union member with at least one non-static data member (if any), exactly one non-static data member has a default member initializer, and each potentially constructed base class of T is const-default-constructible.
For me, it's all about inherent complexity vs incidental complexity. The having to pay attention to lifetimes is just Rust making explicit the inherent complexity of managing values and pointers thereof while making sure there isn't concurrent mutation, values moving while pointers to them exist, and no data races. This is just tough in itself. The aforementioned C++ example is just the language being byzantine and giving you 10,000 footguns when you just want to initialise a class.