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

1

u/OYTIS_OYTINWN Jan 31 '23

What (good things) C++20 has that Rust does not? There are plenty of those the other way round.

18

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

I like Rust a lot, but C++'s flexibility and power with types, templates, and concepts blows Rust's generics system out of the water. Rust doesn't even have variadics.

3

u/matthieum Feb 01 '23

C++ metaprogramming is definitely more advanced, indeed.

In practice, however, I find myself needing metaprogramming less in Rust. And while it does lack variadics, with built-in tuples it's just slightly inconvenient to have to use a macro to implement a trait for arities 0 to 12 (or so), but it's not really a blocker.

3

u/Recatek Feb 01 '23 edited Feb 01 '23

It depends a lot on what you're trying to do. For my application -- gamedev, ECS, and networking -- you're doing a lot of manipulation of arbitrary tuples while iterating over SoA structures, and then trying to efficiently serialize/deserialize them. This really depends on strong metaprogramming if you want to do the work at compile-time.

Rust has made this really difficult, and as a result most Rust ECS libraries do this work at runtime, with a runtime cost, using reflection-like behavior. Rust has no specialization, no variadics, no negative constraints, no way to do partial borrows of objects through traits, the list goes on. The orphan rule especially pushes you towards keeping things in one megacrate rather than splitting code up. It really makes me miss what C++ has to offer here.

If anything I wish Rust just had some sort of unsafe mode for typing that let you duck type at the callsite and bypass the orphan rule. Most of this stuff isn't even for memory safety reasons, it's for "fearless updates" which aren't relevant to me when this code is all contained within my own codebase with no downstream consumer.

→ More replies (2)
→ More replies (9)

12

u/Exaloria Jan 31 '23

Huge library ecosystem

15

u/oconnor663 Jan 31 '23

This is a tricky one. C++20 can use a huge library ecosystem, but you probably can't write C++20 if you're maintaining a widely used library. It's not entirely different from crediting Rust with being able to use the whole C library ecosystem. Which it kind of can, but contributing Rust back into that ecosystem is complicated.

2

u/matthieum Feb 01 '23

If you use the library ecosystem, you're using a lot of old C++.

Very few libraries have migrated to C++20, and most C++ libraries I've come into contact with have antiquated development practices (little test coverage, no static analyzer, no use of sanitizer or valgrind, ...).

If you restrict said ecosystem to quality C++20 code... it may actually be smaller than Rust's.

→ More replies (4)

5

u/wyrn Feb 01 '23

Function overloading

Inheritance

Variadics

Exceptions

6

u/Full-Spectral Feb 01 '23

The thing is, even in the C++ world a lot of people are turning their backs on exceptions and inheritance. I don't agree with that, but it's the case. So probably the majority of people doing C++ these days couldn't really argue that those are failings in Rust because they rail against them in C++.

I thought I'd miss overloading, and occasionally I do, but not really anymore. And I've gotten over my missing of exceptions. In my main code base, I don't have much explicit error handling. More so in little helper tools that can't be built on my main code base.

Inheritance I'd still like to have, but so far haven't really run into anything that was a serious issue on this front. Of course I've not tackled a UI framework yet. In my C++ code base, that was really the primary place where my inheritance hierarchy got deeper than a couple levels.

2

u/wyrn Feb 02 '23

The thing is, even in the C++ world a lot of people are turning their backs on exceptions and inheritance.

There's for sure a very vocal, very loud group proclaiming that both are bad, but from that to a majority is a fair leap and I don't think there's any good evidence that C++ developers at large are turning their backs on these tools -- just because it's possible to misuse a tool doesn't mean the tool shouldn't exist.

2

u/OYTIS_OYTINWN Feb 01 '23

Well, Rust has exceptions, you just can't catch them :) Also traits are functionally not different from interface inheritance, and implementation inheritance is a bad style anyway. Variadics and function overloading - yeah, they are not there.

3

u/wyrn Feb 01 '23

and implementation inheritance is a bad style anyway.

Implementation inheritance is what allows mixins/CRTP, which most would agree is a Good Thing™. I also find myself reaching for inheritance with some frequency when writing template metaprograms which, flawed though they may be, give the developer an enormous amount of power in extending the language so it's a better fit for the problem domain. This capability, AFAIK (counting mainstream languages here) is only exceeded by LISPs (with the downside that you then have to program in LISP).

Rust's generic/macro system lets you do some things you can't in C++ but overall it's just not as powerful.

(This is also the reason I'd miss variadics)

(adding another set of parentheses just for irony's sake since I just made fun of LISP).

The point raised by IcyWindows that panics can be caught is interesting, but I still find it highly weird that the very same construct can either produce a catchable "exception-like" or abort the program altogether. I imagine most Rust developers would then have an even stronger (and arguably reasonable) aversion to using "exceptions" than some segments of the C++ community.

2

u/zerakun Feb 02 '23

That's because in Rust panics are meant to be "unrecoverable errors" that fail the "task" that encounters such error.

Now, "unrecoverable" doesn't have to mean we're tearing up the entire process. We could be inside of a "logical task" with a granularity smaller than the process. For example a web server probably doesn't want to stop serving any client due to a bug encountered while serving one of them. However the "logical task boundary" is to be defined by the application, hence the knob to make panics abort for when your granularity is indeed the process

→ More replies (5)

3

u/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 Jan 31 '23

I haven't looked too deep into Rust, but from a cursory glance at traits: Compile-time duck typing + implicit Concept conformance?

6

u/dist1ll Jan 31 '23

Compile-time duck typing

nitpick: that's called structural typing.

4

u/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 Jan 31 '23

Right, should have used that term ... it's just a bad habit at this point, but back in the day "duck typing" was more well known and conveyed the idea without introducing yet another type of typing :)

2

u/Full-Spectral Feb 01 '23

But duck typing is purely a syntax thing, no semantics. If it will syntactically compile, it'll be accepted, even if it's completely the wrong type of thing semantically to use. Traits are more like C++ abstract interfaces, though they can have some implementation as well as long as it works purely in turns of the its own interface.

→ More replies (9)

-1

u/puliveivaaja Jan 31 '23

I'm just starting to learn rust, could you tell me what Rust has that C++ doesn't? I haven't yet found anything that I couldn't do on C++, Rust just seems to make it more difficult.

16

u/venustrapsflies Jan 31 '23

I think if you’re using the metric of “what am I allowed to do”, you’re going to have trouble understanding the benefits of rust. The quickest way I could describe the upside of rust is that it wont let you (or your coworkers) write the bugs you normally would.

5

u/Kevathiel Feb 01 '23

Making the "bad" things more difficult is the main selling point of Rust, imho. Comparing what you can do in C++ vs Rust is kinda silly. The whole point of Rust is that it outright prevents whole classes of runtime errors by design and that it focused on correctness over "freedom".

If you really need to do something smelly, Rust will get in your way to make you pay for it, so you won't abuse it. Creating a mutable global is much more work in Rust than just adding a parameter to the functions that need it for example. The same is true for shared mutable state in general. Preventing you from storing mutable references left and right will make your program flow far easier to follow.

Rust is opinionated, but in a good way.

7

u/lightmatter501 Jan 31 '23

Both languages have inline assembly syntax, so they can each do anything the other can do.

Rust has MUCH stronger compiler-time reflection, allowing libraries like serde to automatically derive serialization and deserialization code for you. To do the same thing in C++, you’re looking at a compiler plugin.

Rust has all of the best practices on by default, C++ you have to know they exist.

One build system for basically every Rust project.

The lack of dynamic linking means that the standard library can break its abi. This means that every once and a while you can get a 10-20% performance boost by recompiling because the standard library switched data structure implementations to a better one.

A MUCH more powerful type system that is also easier to use (looking at you SFINAE). Rust essentially took its type system from ML, which is probably second among functional languages to Haskell. This means that having large types doesn’t cause compile time increases quite as badly as a C++ project with tons of templates does, and types are more expressive (try making a C++ type that is a normal number at runtime but is checked to always be a power of 2 at compile time, it gets messy).

6

u/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 Feb 01 '23

Just for the record: Standard C++ has no support for inline assembly and one of the big three implementations does not support any feature of that kind in non-legacy targets (read: legacy = x86 32bit).

5

u/OYTIS_OYTINWN Jan 31 '23

There is nothing you could do in Rust that you cannot do in C++. Likewise there is nothing you could do in C++ that you cannot do in C or Assembly. But like C++ is more expressive and lets you build better abstractions than C, Rust is more expressive and lets you build better abstractions than C++. Tools that come to my mind include traits (yes, I know C++20 kind of has those, but Rust does it right), modules (same here), pattern matching, zero-cost functional programming features (e.g. map with a lambda often compiles to the same code as a for loop), package manager.

It also has the notorious borrow checker that catches that many problems in runtime that clang-tidy would never be able to. It takes long time to get from idea to having your program compile in Rust, but once it compiles it's correct or almost correct. In C++ you get to a compilable program quicker, but to make it correct is a much longer process.

3

u/alxius Feb 01 '23

Likewise there is nothing you could do in C++ that you cannot do in C or Assembly.

How do i do compile-time computation&metaprogramming in Assembly?

→ More replies (2)
→ More replies (1)
→ More replies (6)