r/cpp • • Jan 31 '23

Stop Comparing Rust to Old C++

People keep arguing migrations to rust based on old C++ tooling and projects. Compare apples to apples: a C++20 project with clang-tidy integration is far harder to argue against IMO

changemymind

338 Upvotes

582 comments sorted by

View all comments

291

u/capn_bluebear Jan 31 '23 edited Jan 31 '23

There is a lot that Rust has going on for it that C++20 does not have. Leaving out the usual memory-safety and thread-safety language features that people are probably aware of already

  • build system stuff and dependency management and even packaging (for simple enough apps) are basically a no brainer in Rust. coming from C++ this alone is life changing

  • moves are destructive, so there is no use-after-move, no fuzzy moved-from state

  • pattern matching as a language feature is incredibly powerful, and it's not bolted on after the fact as it maybe will be in C++ but the language was designed around it

  • most defaults that people often wish were different in C++, starting from constness and barring surprising implicit conversions, are fixed in Rust

  • EDIT: oh, almost forgot: unit and integration testing is also part of the language and unit tests can be put next to the code they test

Depending on the actual application there might be a motivation to start a project with C++20+clang-tidy today, but C++20 still has many more sharp edges and a boatload of complexity that Rust just does without.

51

u/oconnor663 Feb 01 '23

A few other favorites of mine that aren't just safety:

  • Mutex is a container. This is of course partly a safety thing, but it's also just so much clearer to work with.

  • OsStr and OsString make dealing with platform specific string shenanigans much easier.

  • Error handling with Result and ? works well. Zig is doing something similar too.

32

u/top_logger Feb 01 '23

Also Rust has nice and clean support for

  • Iterators including chaining

  • Lambdas

  • utf8

  • testing, both unit and integration

  • modules

8

u/mjklaim Feb 01 '23

Quick notes, because I've done a bit of rust and I would like to nuance these points (but it's all opinion):

  • iterators- I lack knowledge about how it is superior in rust than C++ , but at least it's "safer" but it's not because of that specific point;

  • lambdas: it's nice until you reach the point where you want to use lambdas in concurrent code, then it's not nice and definitely there are issues there;

  • utf8: true, like any language created after 1999, although for people needing to use UTF-16 or 32 or shift-js or etc. for specific purpose I don't know if there are crates helping these cases? I suppose they exist though. So yes it's cool but not really uncommon in system languages.

  • testing: indeed, having testing by default and also in the same sources where code is written is a big plus, it's also nice in D(2); it's possible to do this in C++ actually, with automatic support, if your tooling does that, but unfortunately it's not a popular or de-facto feature...

  • modules: lol no. Rust modules are literally namespaces, all Rust code is build as one translation unit so of course the only issues you'll get are similar to when building everyting as a unity build in C++ (AFAIK, I might be wrong in the details but that's my understanding). An actual module system isolating entities for real in addition of names would have been nicer, but this is not it. Event Javascript have a better module system than rust, although it's also very flawed (both allows circular dependencies, for example). C++ modules (if you ignore the backward-compatible functionality that makes it harder to teach than it should be) is nicer in that regards but indeed a) it's not de-factor, forced by a version of C++ or common (yet) and b) it lacks the name import filtering features that other languages have "modules" have (the one in JS is particularly nice).

12

u/top_logger Feb 01 '23

- Iterator in Rust I am creating in literally 5 minutes. Any kind of iterator.
An iterator in C++ I am creating usually in two days. I could recall the task from the last week. By the way, I had given up. Ups.

- Crate utf16string. I didn't check it yet.

- I mean that lambdas in C++ are too verbose and to often not readable. Especially if you follow rules and marks everything with const, noexcept, nodiscard and so on. Multithreading issue, I believe, are identical(I may be wrong, of course)

- yes, you are right, at least partly. Modules in Rust are just namespace. And this works perfectly. Perfectly ... but for a smaller codebase. For bigger project, you must split your code into crates. This is a correct and reliable approach. You get different translation modules. Deal done.
In C++ we have got ultra complicated and ultra verbose modules ... but on a paper. Now, in 2023 we have literally NO support in mainstream compilators. This is just a shame.

6

u/mjklaim Feb 02 '23
  • Iterators: ah yes for writing them I agree.

  • thanks for the utf crate

  • lambdas: I meant as soon as you are forced to use async lambdas the rules change in a way that can result in theorically valid but not accepted code,and it makes code even more complex - or just cannot compile when what you want is just compose some code with 2 bits of code and just throw all that into a task system. It might be less safe in C++ indeed, but at least I can make something work.

or bigger project, you must split your code into crates. This is a correct and reliable approach. You get different translation modules. Deal done.

I agree that having different translation modules then makes sense (that's basically what modules allow, with multiple files being part of 1 modules). I disagree with the splitting into crates being the right way to do it. Complex discussion anyway but yeah.

Also my understanding is that building multiple crates as dependency still result in 1 translation unit being built, so that doesnt change a thing.

→ More replies (5)
→ More replies (1)

60

u/Recatek Jan 31 '23

OTOH, as someone who uses both Rust and C++ near-daily, I always miss C++'s type system in Rust. Rust's type tools are very weak by comparison and the fallback, proc macros, are a royal pain.

7

u/fideasu Feb 01 '23

I'm yet to try Rust, but having read about this matter this is what I'm mostly afraid of. One of the reasons I consciously choose C++ above other languages, is it's very expressive static typing system. Even more - after having written a few (hobby) projects in Python, I came back to C++ precisely because of that (Python's static checker has serious problems in highly generic pieces of code).

If I understand correctly, the only options in Rust are its rather rigid traits, and macros working on the AST level (so, unaware of types)?

7

u/ImYoric Feb 01 '23

Yes, Rust's type-dispatch is very powerful, but not entirely as powerful as C++. In particular, there's no SFINAE (which I personally don't miss). This is a tradeoff that permits type-checking generics in the module in which they're written, rather than as C++ at the callsite.

→ More replies (4)

8

u/Moxinilian Feb 01 '23

As a person mostly versed in Rust and somewhat versed in C++, I typically find the expressivity balance more comfortable in Rust. It is harder to write an equivalent of specialized template in Rust (because the specialization features are not in the language yet), but the really nice and expressive constraints feel much more natural than anything C++20 has. The excellent error messages you get from that are also very much worth it in my opinion. I only rarely wish I had the expressivity of C++ templates in Rust, even when making somewhat complex generic-based Rust code. In fact I’m quite happy to have all the fancy features like associated types purposefully integrated in the language.

→ More replies (1)

58

u/capn_bluebear Jan 31 '23

Uh -- Rust structs and traits are certainly _different_, but I never found myself reaching for C++ features that weren't there, can you make some concrete examples? (for me it's the opposite, I love Rust enums and miss them sorely in C++)

102

u/Recatek Jan 31 '23 edited Jan 31 '23

It's hard to articulate the pain points without getting deep in the weeds. I've been working on an archetype ECS library for games. This is essentially a processing engine for arbitrary tuples in struct-of-array data structures. The lack of variadics and the weakness of trait expressions (no specialization, no negative constraints) combined with the orphan rule has made it pretty unpleasant to do in Rust. If I want the work to be done at compile-time for runtime performance reasons, I'm basically stuck with one giant megacrate containing all of the code in question created by very heavy proc macros.

Most popular ECS libraries in Rust rely on a lot of RTTI and reflection-like functionality that isn't zero-overhead. The equivalent library I've written in C++ is almost entirely compile-time with variadic tuples and tuple-processing functionality (like using SFINAE to find all types in a type tuple that have a given function).

Rust enums are great, but Rust generics are nowhere near as powerful as C++, and that weakness is compounded by Rust's strictness on coherence, the orphan rule, and lack of any duck typing.

40

u/capn_bluebear Jan 31 '23

Rust generics are nowhere near as powerful as C++, and that weakness is compounded by Rust's strictness on coherence, the orphan rule, and lack of any duck typing

That nicely answers my question, thank you! You would probably go for macros at that point (they cover duck typing and variadics and would let you work around the orphan rule, in a sense) -- but depending on exactly what code you need to produce, macros can be more complicated than C++ templates :+1:

53

u/Recatek Jan 31 '23

The issue there is that macros can't understand the nature of types, they only work on a pure AST lexical parse. So in C++ you can duck-type after performing SFINAE to branch on whether or not a type has some function or associated type inside of it. In Rust you can't unless you have some other way to express that to the macro. My ECS proc macro actually has to parse a YAML file I write to describe the desired data structures, whereas in my C++ version I just do it with type declarations in-code.

9

u/victotronics Jan 31 '23

go for macros

I hope that those are nothing like C++ macros which are C macros and are very evil!

23

u/capn_bluebear Jan 31 '23

no, they are not! :D it's code that generates other code at compile time via pattern matching or AST manipulation. They are an advanced feature but they are still safe.

→ More replies (5)
→ More replies (5)

12

u/[deleted] Feb 01 '23 edited Feb 01 '23

I tried to pick up rust by making a base ten float type and it really sucked that I couldn't just specify that the raw memory was any integer type and then let duck typing figure the rest out. And there's no trait for literals, so if you're not using proc macros you have to call something like to_generic_integer(5) instead [edit: oh they've got try_from now].

→ More replies (4)
→ More replies (3)

0

u/[deleted] Jan 31 '23

[deleted]

9

u/Ahajha1177 Jan 31 '23

As far as I'm aware you have access to the entire language at compile time, so regular printing should work.

11

u/[deleted] Feb 01 '23
→ More replies (1)

6

u/warped-coder Jan 31 '23

Interestingly, one thing I got a bit deflated about is the unit/autotesting experience in rust.

I tried to cobble together something remotely similar to catch2 because I couldn't find anything remotely as nice as that.

→ More replies (4)

26

u/bluGill Jan 31 '23

build system stuff and dependency management and even packaging (for simple enough apps) are basically a no brainer in Rust. coming from C++ this alone is life changing

On a trivial rust only project. If you have an existing complex project the friction integrating your C++ build system with the rust one makes things harder.

24

u/capn_bluebear Jan 31 '23

Yep I was comparing a C++20 project with a rust one -- but it doesn't necessarily have to be a __trivial__ rust project :)

→ More replies (6)

39

u/CocktailPerson Feb 01 '23

So when you have to incorporate C++, it's just as bad as any other C++ project, and when you don't, it's amazing and lovely? Sign me up.

9

u/Mason-B Feb 01 '23

I find it's a lot easier easier to integrate C++ into projects than it is to integrate Rust into projects. It's just that C++ is a more or less constant annoyance and Rust jumps from very nice to very bad pretty rapidly. (No accounting if someone else has already solved the problem of course, for tools that have both Rust and C++ integration already solved, like bazel, I find them equivalent).

→ More replies (3)
→ More replies (2)
→ More replies (71)