Comment by timschmidt

12 hours ago

In my experience (20+ years with C/C++, and about 4 years with Rust), Rust is significantly less complex than C++, while being similarly capable. The extra syntax that throws off so many C++ devs is almost exclusively about data types and lifetimes, which I find very useful for understanding my own code and others', and which I wish I had in C++.

Some of this is just knowing from experience, by the time C++ programmers knew they wanted the destructive move assignment semantic at the turn of the century they already had large codebases which relied on C++ copy assignment, so, too bad. It took a significant extra effort to land C++ 11 move semantics, which are still less useful but also have worse ergonomics. Whereas Rust knew it wanted the destructive move so, that's just how everything works in Rust.

But there are a bunch of unforced errors in C++ design beyond that. Default implicit conversion is a choice and a mistake. Multiple inheritance is a mistake, Stroustrup even says he did it because it was easy, which is exactly the same cause as Hoare's NULL. Choosing "All correct programs compile" (the other option was "No incorrect programs compile", you can't have both, see Henry Rice's PhD thesis) was a mistake. My favourite bad default in C++ is atomic memory ordering. The nasty trick here was that picking a default was the mistake, it's not that they picked the wrong one but that they picked a default at all. C++ programmers end up writing code which doesn't specify the ordering even though the ordering was their only important decision.

  • Agreed. C++ has advanced incredibly over the years, with the developers putting in immense, important, and useful effort. But Rust has had the benefit of a fresh start with lessons learned. People who have yet to understand the choices made in it's design see the differing semantics as unnecessary hurdles, whereas people who've taken the time to learn why those choices were made and what adhering to them enables find themselves enamored of their newfound abilities. It's why there's such an intense communication rift between folks on either side of the experience.

  • > Choosing "All correct programs compile" (the other option was "No incorrect programs compile", you can't have both …

    This is really the important distinction between C++ and Rust.

    In my opinion, it seems easier to complement the former to catch issues afterwards (like this article) than it is to design a language that does not require you to jump through hoops to get something correct to compile.

    I hope programming language design progresses to a state that makes my point invalid, but the “bro rust is easier than C++” gaslighting culture does not help.

    • I'm quite experienced at C++ and not that experienced in Rust... but I believe that writing correct Rust is easier than writing correct C++. People get C++ to compile alright, but it often has problems at runtime. You need to know what you are doing in both, but C++ allows you to compile with certain classes of bugs anyway. Even experts still occasionally introduce bugs in C++ that Rust wouldn't allow.

      1 reply →

    • > In my opinion, it seems easier to complement the former to catch issues afterwards (like this article)

      Fil-C of course can't magically fix your incorrect program. It never had any defined meaning, but the compiled executable does something and Fil-C will ensure that if the thing it does involves say, a use-after-free at runtime now it exits reporting the error, but it can't fix the fact it's nonsense, that's not their purview.

      There's no point in hoping that somehow Programming Languages will overturn Mathematics. I mean, I can't blame you for trying, Bjarne Stroustrup is a professor and still seems to think that should be attempted, but it's futile. We're definitely talking "Why can't I extinguish the sun with water?" level thinking.

      Obviously I can't speak to your own experience but for me certainly Rust is easier than C++.

      2 replies →

Great, if you are right everyone is going to be using Rust eventually.

I like the concepts proposed by Rust but do not like fighting with the borrow checker or sprinkling code with box, ref, cell, rc, refcell, etc.

At some point there’s going to be a better designed language that makes these pain point go away.

  • > I like the concepts proposed by Rust but do not like fighting with the borrow checker or sprinkling code with box, ref, cell, rc, refcell, etc.

    I'm not sure why this would be confusing or disliked by a C++ dev.

    Rust's Box<T> is similar to C++'s std::unique_ptr<T>.

    Rust's Rc<T>, Arc<T>, and Rc<RefCell<T>> serve similar uses to C++'s std::shared_ptr<T>.

    Rust's Weak<T> is similar to C++'s std::weak_ptr<T>.

    Verbosity of both is nearly identical. The big difference is that Rust enforces the rules around aliasing and mutability at compile time, whereas with C++ I get to find out I've made a mistake when my running code crashes.

    • Reminds me of “A monad is a monoid in the category of endofunctors, what's the problem?”

      As you write this, do you not start to see why this would be confusing?

      Yes, C++ is pretty bad at this too.

      The fact that these exist and the programmer has to always be consciously be aware of it is an indication that something has gone wrong in the language design.

      Imagine if you were to write C code in 1970, but you always had to keep track of which registers each variable corresponded to. That is how I look at these.

      (There were, indeed, early 'high level' languages that required you to do this :)

      11 replies →

  • > Great, if you are right everyone is going to be using Rust eventually.

    Every task does not need speed and safety. Therefore, "everyone" doesn't need Rust.

    But I could easily see a future where C++ is relegated to legacy language status. It has already had decades of garbage-collected languages chipping away at most of its general-purpose uses, but Rust seems capable and in a position to take away most of its remaining niches.

    It's kind of why the old C++ programmer that I am decided to learn Rust in the first place - seemed like a good idea at the time to skate where the puck is heading.

    • > But I could easily see a future where C++ is relegated to legacy language status.

      Yes, agreed. My prediction is that the replacement is a friendly language that makes Rust's ideas ergonomic to use.

      2 replies →

The real answer should have been a new language that has memory safety without all the extra conceptual changes and orthogonal subsystems that Rust brings. The core value of safety did not need the reinvention of everything else with the accompanying complexity and cognitive load. For example Zig which instead of introducing a new metaprogramkming language, it uses...... Zig - imagine using the same language instead of inventing a new additional language with all the accompanying complexity and cognitive load and problems. Rust is for those who revel in complexity. And traits - traits and extra complexity not needed for safety. And result and option and move by dedfault - none of these things were needed but they all add up to more complexity and unfamiliarity and cognitive load. And when you add it all together and intertwine it you end up with something so unfamiliar that it no longer looks like "ordinary programming" it looks like something from the Cambrian period.

  • As a C++ developer, my experience with learning both Rust and Zig is that they're both good languages, and any reasonably skilled C++ developer could learn either language if they put their mind to it.

    If you forced me to pick between Zig and Rust for a long-running project though, I'd pick Rust 10/10 times for the simple fact that it has been stable for more than a decade and already has momentum and funding behind it. Zig is a cool language - one that I've actually written more of than Rust - but it hasn't hit 1.0 yet and still has significant churn in both the language and standard library.

    • My point is this:

      Rust is complex.

      That is not a statement anyone can deny. Unless you're a Rust-bro "hey man I learned it so I’m baffled how you can't see it's super simple ..... etc etc" - often the implication that you're not very smart if you think Rust is complex.

      Of everything that I have learned about programming, this is the BIGGEST lesson: - complexity is bad, avoid complexity. Complex is not the same as "sophisticated", which implies necessary intricacy. Complex means unneeded unnecessary cognitive load - things made harder than they should be when it could have been avoided - that's complexity. If you are writing complex code then you're writing bad code. And Rust is complex.

      It is sad that we did not end up with a SIMPLE programming language that solves the memory safety problem.

      What we needed was Rust-- i.e Rust without all the non-safety related extras that make it different - it's all the non-safety add ons that makes Rust into a Rube Goldberg machine.

      8 replies →

  • Isn't Zig's repetitive ceremonial code around allocators+ allocation + defer *.deinit() a sign of a serious shortcoming like golang's error handling? If zig is so good at metaprogramming, why isn't there a metaprogramming solution to this repetitive code?

    • Memory allocations are always done explicitly (nothing is hidden or implicit). I've not written enough Zig yet to appreciate that, but I've hit plenty of those issues year-after-year with C++ to know their approach is sane and rational.

      1 reply →

  • > The real answer should have been a new language that has memory safety without all the extra conceptual changes and orthogonal subsystems that Rust brings. The core value of safety did not need the reinvention of everything else with the accompanying complexity and cognitive load.

    What would the minimal set of features be, in your opinion?

    > For example Zig which instead of introducing a new metaprogramkming language, it uses...... Zig - imagine using the same language instead of inventing a new additional language with all the accompanying complexity and cognitive load and problems.

    Zig probably isn't the best comparison since Zig doesn't try to achieve the same level of compile-time memory safety guarantees that Rust aims for. For instance, Zig doesn't try to statically prevent use-after-frees or data races.

    That being said, as with everything it's a question of tradeoffs. Zig's metaprogramming approach is certainly interesting, but from what I understand it doesn't offer the same set of features as Rust's approach. For example:

    - Zig's generics are more similar to C++ templates in that only instantiated functions are fully checked by the compiler. Rust's generics, on the other hand, are completely checked at the definition site so if the definition type-checks the author knows it will type-check for all possible instantiations. Rust's approach also lends itself to nicer error messages since everything a generic needs is visible up front.

    - Zig's comptime isn't quite 1:1 with Rust's macros. comptime is for... well, compile-time computation (e.g., reflection, compile-time branching, or instantiating types). Macros are for manipulating syntax (e.g., code generation or adding inline support for other languages). Each has things the other can't do, though to be fair there is overlap in problems they can be used to solve.

    In any case, metaprogramming approaches are (mostly?) independent of memory safety.

    > And result and option and move by dedfault - none of these things were needed but they all add up to more complexity and unfamiliarity and cognitive load.

    I don't think Result/Option are that complex (if at all) since they're trivially derivable from discriminated unions/sum types/enums.

    I'm also not sure how move by default is necessarily "more complexity... and cognitive load"? Maybe as a result of unfamiliarity, perhaps, but that seems more a property of a person than a language, no?

  • > The real answer should have been a new language that has memory safety without all the extra conceptual changes and orthogonal subsystems that Rust brings.

    So what you're saying here is that you don't understand that Rust's rules around memory ownership, aliasing, and mutability are what allow the language to provide deterministic compile time memory safety without runtime cost. If you figure out another way to guarantee memory safety at compile time with zero runtime overhead, you should write a paper and start another language around it!

    https://en.wikipedia.org/wiki/Capability_Hardware_Enhanced_R... exists, and is an exciting, laudable effort, I think. But requires hardware support as well as language modifications.

    • And even if you a runtime solution with no runtime cost, you'd still need to run the code, to find the memory safety bugs. Static analysis is supposed to tell you there is no path that violates memory safety.