How is Rust less complicated than C++? I don't use it, but from what I read it seems to be even more complicated, and getting even more so with the myriad of features they are adding each release.
And with respect to non-library features currently in-flight, by and large they are "playing catch-up" to C/C++ for compatibility or to fulfil use cases for which the language is not currently convenient or sufficient e.g. generics-over-values, const functions, allocators.
Rust has an upfront feeling of complexity in lifetimes and the borrow checker, but here's the ugly truth: pointer lifetime issues don't exist any less in C++, the only difference is the compiler doesn't help you with them.
Being too limited for the intended purpose is still being wrong on some level. Not a bug, but still.
Granted, sometimes you don't care: stuff like `true?1:"foo"` is of type int an has value 1, but the compiler will rejected because of a type mismatch in the conditional—but you don't care because the code smell is too big and obvious to ignore.
Sometimes however you do care, if only a little: the value restriction in ML for instance prevent some function from being polymorphic because some side effect might break the type system. This forces you to eta-expand the function definition (no partial application, you need to write the arguments explicitly), which is a bit of a hassle in some cases.
> Being too limited for the intended purpose is still being wrong on some level. Not a bug, but still.
By that criteria, every single statically typed language is "wrong on some level", you'll always find something you want to do/express which a specific language's compiler will not accept. That's not very helpful.
> Sometimes however you do care
I'm not saying people should not care, caring about lexical lifetimes being a pain in the ass is perfectly sensible (there's a reason why non-lexical lifetimes are being implemented after all). I'm saying there's a gulf between "the compiler does not allow X" and "the compiler is wrong", and lexical lifetimes are the former.
Well, most compiler bugs are cases where the compile "is wrong" and "being too limited for what I'm trying to do" also sounds like a bug. So this reads to me as "aside from when they are wrong, compilers are not wrong" ;)
> Well, most compiler bugs are cases where the compile "is wrong"
Most compiler bugs are situations where the compiler allows stuff it should not or generates incorrect code.
> "being too limited for what I'm trying to do" also sounds like a bug. So this reads to me as "aside from when they are wrong, compilers are not wrong" ;)
Most every compiler is "too limited" to do some things, that is a big reason why dynamically typed languages are still a thing. For instance I can't tell Java to just take any object with an attribute "foo" despite it being the only thing I want to use (and I don't even care about its type) — bypassing the compiler via reflection aside. Do you think that's a bug in the compiler?
In C++, creating an instance of a class is fantastically complicated. The class must be initialized, and there is a zoo of different initialization forms: value, direct, aggregate, default, list, copy, etc. Which one foo {} invokes has been the subject of a spec bug. Some of these invoke a constructor, which is like a function, but isn't a function, multiplying the number of concepts involved further. Constructors suffer from bizarre syntactic limitations. They need a special syntax for handling exceptions. The form foo f(); famously declares a function instead of default initializing f. The [dcl.init] section of the spec is about 16 pages long and there are about another dozen about how constructors work in the special member functions section.
In Rust, there is exactly one way to create an instance of a struct: you provide a value for each of its fields.
> In Rust, there is exactly one way to create an instance of a struct: you provide a value for each of its fields.
As a user of both rust and and C++, I certainly wouldn't consider the the lack of default constructors in rust to be a good thing. Especially for std types like Vec it is really annoying to have to initialize it explicitly.
> The [dcl.init] section of the spec is about 16 pages long and there are about another dozen about how constructors work in the special member functions section.
That is a bad argument if you're comparing to a language that doesnt have a spec. Maybe a complete, exhaustive rust spec would be much longer than the C++ standard?
> As a user of both rust and and C++, I certainly wouldn't consider the the lack of default constructors in rust to be a good thing. Especially for std types like Vec it is really annoying to have to initialize it explicitly.
That is fair but orthogonal to the issue at hand, namely:
A good, but difficult question - it's difficult to quantize complexity. Both languages have aim that their users can have much as much control as possible, even if it entails complexity in the language desing.
Perhaps one answer could be, that much of Rust's complexity arises from the concepts of 'borrowing' and 'lifetimes' and how they are encoded in the language.
In C++, complexity much of the complexity arises from the copy/reference semantics, and the possibility of overriding standard behaviour. You need to have wider context to understand local code. So you need to understand how the C++ works on quite a low level and you might need to know more specifics on your C++ codebase than might be the case in Rust.
It seems to have many fewer language-level features. No lvalue/rvalue distinction, pointers are not elevated to a language-level feature (only references), only a very limited exception mechanism (panic) that doesn't try to generalize to support general validation (rather general validation is done with Result which is a plain old datatype written in the language, though admittedly it relies on a macro for use - more generally this approach is also used for things like I/O, a lot more is done with plain old library functions written in the language rather than added to the language standard), the macro mechanism is much less of a special case than the C++ preprocessor, no "const" and associated language-level complexity (e.g. "mutable"), smaller and more consistent syntax (no comma operator, no sequence point rules), more unifying things from the start (e.g. traits as values) rather than ad-hoc conversion rules in the language later on.
> Result which is a plain old datatype written in the language, though admittedly it relies on a macro for use
It does not. It relies on a special construct (!) or macro (try!) for convenience, but these are not necessary (a popular alternative is to use the various HoFs instead) and desugar to pretty trivial (if possibly repetitive) code:
For one thing, Rust has had the benefit of learning from the evolutionary lessons that languages like C++ went through. Another point is that really arcane stuff that's applicable only to very few legacy systems need not be supported, and aren't, while C and C++ don't have the luxury of dropping such support.
Rust mostly avoided accidental complexity. The complexity it has is mainly from tackling a complex problem of providing memory safety and preventing data races at compile time.
In C++ a large chunk of complexity comes from legacy of being a C superset and having to preserve backwards compatibility with all of its old features and syntax quirks, and often surprising edge cases arising from interactions between different features.
In pretty much all senses of the word?
> getting even more so with the myriad of features they are adding each release.
It's far from adding a "myriad of features" with each release, and most of those it adds are library stuff, see for 1.23: https://github.com/rust-lang/rust/blob/master/RELEASES.md#ve...
And with respect to non-library features currently in-flight, by and large they are "playing catch-up" to C/C++ for compatibility or to fulfil use cases for which the language is not currently convenient or sufficient e.g. generics-over-values, const functions, allocators.
Rust has an upfront feeling of complexity in lifetimes and the borrow checker, but here's the ugly truth: pointer lifetime issues don't exist any less in C++, the only difference is the compiler doesn't help you with them.