Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is one of the (several?) things that make me very worried about Rust long-term. I love the language, and reach for it even when it sometimes isn't the most appropriate thing. But reading some of the made-up syntax in the "Removing Coherence" section makes my head hurt.

When I used to write Scala, I accepted the fact that I don't have a background in type/set/etc. theory, and that there were some facets of the language that I'd probably never understand, and some code that others had written that I'd probably never understand.

With a language like Rust, I feel like we're getting there. Certain GAT syntxes sometimes take some time for me to wrap my head around when I encounter them. Rust feels like it shouldn't be a language where you need to have some serious credentials to be able to understand all its features and syntax.

On the other end we have Go, which was explicitly designed to be easy to learn (and, unrelatedly, I don't like for quite a few reasons). But I was hoping that we could have a middle ground here, and that Rust could be a fully-graspable systems-level language.

Then again, for more comparison, I haven't used C++ since before they added lambdas. I wonder if C++ has some hairy concepts and syntax today on par with Rust's more difficult parts.

 help



> I wonder if C++ has some hairy concepts and syntax today

Both better and worse.

The current version of idiomatic C++ is much cleaner, more concise, and more powerful than the version of C++ you are familiar with. You don't need C-style macros anymore. The insane template metaprogramming hacks are gone. Some important things that were problematic to express in C++ (and other systems languages to be fair) are now fully defined e.g. std::launder. C++ now has expansive compile-time programming features, which is killer for high-performance systems code, and is more expressive than Rust in important ways.

The bad news is that this was all piled on top of and in addition to the famous legacy C++ mess for backward compatibility. If you are mixing and matching ancient C++ with modern C++, you are going to have a bad time. That's the worst of all worlds.

But if you are lucky enough to work with e.g. an idiomatic C++20 code base, it is a much simpler and better language than legacy C++. I've done a few major C++ version upgrades to code bases over the years; the refactored code base was always smaller, cleaner, safer, and easier to maintain than the old version.


These problems will happen in some form for every long lived codebase. In 15 years you will have some regrets, and some of those regrets will be things that are all core APIs used everywhere in the code base and so impossible to quickly change.

It's much simpler and better than it used to be but it's still pretty bad. As just one example off the top of my head consider the meaning of curly braces for initialization. There's several different things they can mean depending on the context. Good luck figuring out which set is currently in effect.

The initialization situation in C++ is indefensibly broken. It is near the top of my list of things I hate about C++.

You can mitigate it with some practices but that this is even necessary is a crime. Initialization is one of the most basic things in software development. How do you fuck it up so badly?

On a day to day basis it doesn’t cause me issues but it offends me just on principle.


Use static analyzers and move on. Almost all the complaints I see about C++ nowadays are removed by max warning levels. Set them as error.

Certainly initialization is the single most confusing feature in C++, I can give you that.

But still doable with s few patterns to remember. And warnings always max level.


I still use Eclipse CDT and its static analysis is running in real time, as you type code, which is killer. Combined with Valgrind integration, I don't see myself moving on anytime soon.

Is Eclipse CDT still good these days? Wow did not hear of it for a while. I thought C++ support was not maintained anymore.

I use CLion mostly but I never stop coming back to Emacs+LSP.

And yes, the analysis is quite competitive tbh. People often talk about this weird thing or the other in C++ but the experience is quite better than what the ISO standard strictly has to offer.


> But reading some of the made-up syntax in the "Removing Coherence" section makes my head hurt.

Articles discussions new features always have difficult syntax. There have been proposals like this going on from the start.

Fortunately the language team is cognizant of the syntax and usability issues with proposals. There have been a lot of proposals that started off as very unwieldy syntax but were iterated for years until becoming more ergonomic.


I think it's more than the syntax, it's just the number of concepts you need to keep in your head to read a type signature, or a trait declaration, or whatever. There's only so much you can pack into a limited language before it becomes too much to keep in your head at once.

Most of the time over the past couple years when somebody complained about Rust getting a complex new feature, it was complex backend work that exhibited to users as the removal of restriction, making the language simpler. Example: async closures. A language that supports both async and closures but doesn't support them together is more complicated than one that supports async closures.

This feels like it should end up similar. The driving desire here is the removal of a restriction, so hopefully it ends up as an end user simplification rather than complication.


And then someone declares "this language is a mess! I'll take the good parts and create a new one without all this cruft!" and the cycle continues

> I wonder if C++ has some hairy concepts and syntax today on par with Rust's more difficult parts.

… … … … Unqualified name lookup has been challenging in C++ since even before C++11. Overload resolution rules are so painful that it took me weeks to review a patch simply because I had to back out of trying to make sense of the rules in the standard. There's several slightly different definitions of initialization. If you really want to get in the weeds, starting playing around with std::launder and std::byte and strict aliasing rules and lifetime rules, and you'll yearn for the simplicity of Rust.

C++ is the absolute most complex of any of the languages whose specifications I have read, and that's before we get into the categories of things that the standard just gives up on.


> starting playing around with std::launder and std::byte and strict aliasing rules and lifetime rules, and you'll yearn for the simplicity of Rust

Annotations like std::launder, lifetime manipulation, etc solve a class of problems that exist in every systems language. They inform the compiler of properties that cannot be known by analyzing the code. Rust isn't special in this regard, it has the same issues.

Without these features, we either relied on unofficial compiler-specific behavior or used unnecessarily conservative code that was safe but slower.


> Rust isn't special in this regard, it has the same issues.

This is both fundamentally true and misleading. Rust has to solve the same issues but isn't obliged to make all the same bad choices to do that and so the results are much better.

For example C++ dare not perform compile time transmutations so, it just forbids them and a whole bunch of extra stuff landed to work around that, but in Rust they're actually fine and so you can just:

    const FOO: bool = unsafe { core::mem::transmute::<i8, bool>(2) };
That blows up at compile time because we claimed the bit pattern for the integer 2 is a valid boolean and it isn't. If we choose instead 0 (or 1) this works and we get the expected false (or true) boolean instead of a compiler diagnostic.

C++ could allow this but it doesn't, rather than figure out all the tricky edge cases they just said no, use this other new thing we made.


> For example C++ dare not perform compile time transmutations

I am confused by this assertion. You can abuse the hell out of transformations in a constexpr context. The gap between what is possible at compile-time and run-time became vanishingly small a while ago.

I think your example is not illustrative in any case. Many C++ code bases work exactly like your example, enforced at compile-time. That this can be an issue is a hangover from retaining compatibility with C-style code which conflates comparison operators and cast operators. It is a choice.

C++ can enforce many type constraints beyond this at compile-time that Rust cannot, with zero effort or explicit type creation. No one should be passing ints around.


First of all mem::transmute is like bit_cast (which works perfectly fine in constexpr context), not reinterpret cast.

Second, this compiles just fine:

   constexpr int ivalue = 1;
   constexpr bool bvalue {ivalue};
This fails at compile time (invalid narrowing):

    constexpr int ivalue = 2;
    constexpr bool bvalue {ivalue};
Note we don't need bit_cast for this example as int to bool conversions are allowed in C++.

Surely "We have many different ways to do this, each with different rules" is exactly the point? C++ 20's std::bit_cast isn't necessarily constexpr by the way although it is for the trivial byte <-> boolean transmutation I mentioned here.

I see that C++ people were more comfortable with the "We have far too many ways to initialize things" examples of this problem but I think transmutation hits harder precisely because it sneaks up on you.


bit_cast and reinterpret_cast do different things: one works at the value level, the second preserves address identity (and it is problematic from a aliasing point of view).

Not sure what any of this has to do with initialization though.


In my experience conversions is one of the things that maximum warning levels do excellent static analysis for nowadays. In the last 15 years I hardly had a couole problems (init vs paren initialization). All narrowing etc. is caught out of the box with warnings.

I'm not sure what you're getting at but

const bool z = (const bool)((int8_t)2);

Is perfectly valid C++.


That's a conversion, not the same. The naive equivalent to transmute would be

    int8_t x = 2;
    bool y = *reinterpret_cast<bool *>(&x);
But reinterpret_cast isn't valid in a constexpr scope.

My point is, in your exact example both reinterpret_cast and C-style casts have the exact same behavior, making the example bad. If you want to showcase a deficiency of C++, it would make sense to pick something where the difference between cast types actually matters.

> But reinterpret_cast isn't valid in a constexpr scope.

std::bit_cast is


Oh cool, and it behaves like memcpy, not like pointer aliasing! I'm stuck with C++14 at work so I missed that one.

The right strategy to use C++ efficiently is to set warnings to the maximum as errors and take the core guidelines or similar and avoid past cruft.

More often than not (except if you inherit codebases but clang has a modernize tool) most of the cruft is either avoidable or caught by analyzers. Not all.

But overall, I feel that C++ is still one of the most competitive languages if you use it as I said and with a sane build system and package manager.


Unless you're writing a compiler, you should require the author of the patch to explain why it works.

This was a patch for the compiler implementation of the changes to the standard.

Having used Rust professionally for six years now, I share your fear. Like many of the commenters below, coherence just hasn't been a big problem for me. Maybe there are problem spaces where it's particularly painful?

How does the Rust language team weigh the benefits of solving user problems with new language features against the resulting increased complexity? When I learned Rust, I found it to be quite complex, but I also got real value from most of the complexity. But it keeps growing and I'm not always sure people working on the language consider the real cost to new and existing users when the set of "things you have to know to be competent in the language" grows.


I really wouldn't worry much. Over the last decade of rust, very few of the articles exploring new syntax have turned into anything controversial by the time they were merged (I can't even think of big rust syntax changes other than `impl T` in arguments etc). The singular example really is async/await and, having been quite worried about it / against it at the time, it was really nothing to be concerned with at all.

> I wonder if C++ has some hairy concepts and syntax today

https://tartanllama.xyz/posts/cpp-initialization-is-bonkers/


Reflection syntax (C++26 I think) has made my 30+ years-of-C++ brain melt.

It's not insane, it's just ... melt-inducing.


Yeah, for me reflection and coroutines were the first changes to C++ where the implementation and use mechanics weren't immediately obvious by reading a few references. It requires a bit of proper study to wrap your head around it.

I agree. It was the first time I actually thought 'who the hell writes code like this?'

Rust opened the door to innovation in the low-level languages space, but as long as it is already the most theoretically advanced practical language there, it will always attract the audience that actually wants to push it further. I don't know if there is a way to satisfy both audiences.

A similar thing happened with C++: The fact that it had a relatively high complexity of interacting features (already 30+ years ago) that you could use to do smart things, did attract smart people with a high tolerance for complexity. And with such an audience, complexity tends to expand up to the limits of tolerance (or even a little beyond).

Rust had a better start, not the least because it wasn’t designed on top of an existing language like C++ was, but who knows what it will look like in 30 years.


I think there is: a schism. Another language, inspired, intelligible and interoperable with Rust, but with other goals, likely ease of use or surface simplicity. In my mind it would be pretty much the same as Rust, but whenever a compile error gives you a suggestion in rustc would instead compile (and at most be a warning in this hypothetical language). Migrating from Rust to this language would be changing a single setting in Cargo.toml. The other way around would be fixing a bunch of compile errors. You could use the entire crate ecosystem in a native way. This language could also serve as a test bed for features that might or might not be suitable for Rust. It can also have a more aggressive evolution schedule, meaning that it wouldn't be perma-1.x, so it can be bolder on what is attempted.

There is precedent: with type checkers like pyright you can opt into specific checks, or have a basic, standard, strict setting, each expanding the set of checks done.

How would dependencies work in this schism? E.g. if serde starts using named impls, do all dependencies have to use named impls?


I'd take `Rust with a GC and specialization` over current Rust any day.

I mean… Sure, if we’re just making stuff up, a compiler that can magically understand whatever you were trying to do and then do that instead of what you wrote, I guess that’s a nice fantasy?

But out here on this miserable old Earth I happen to think that Rust’s errors are pretty great. They’re usually catching things I didn’t actually intend to do, rather than preventing me from doing those things.


> But out here on this miserable old Earth I happen to think that Rust’s errors are pretty great. They’re usually catching things I didn’t actually intend to do, rather than preventing me from doing those things.

As it happens, you are replying to the person who made Rust's errors great! (it wasn't just them of course, but they did a lot of it)


I bow to them and thank them for their service!

> I wonder if C++ has some hairy concepts and syntax today on par with Rust's more difficult parts

Yes, it's called "C++".


I wonder if Google's Carbon could fill that gap?

https://en.wikipedia.org/wiki/Carbon_(programming_language)


I wonder how Zig compares here

> But reading some of the made-up syntax in the "Removing Coherence" section makes my head hurt.

It's interesting to note the discrepancy between replies to this blog here and, say, lobste.rs (which is neutral to it).

Here it's very concerned about complexity, while on lobsters it's mostly about needing this feature - yesterday.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: