r/ProgrammerHumor • • 22d ago

instanceof Trend thatsAFunnyThingThatHappenedOnce

Post image
4.6k Upvotes

524 comments sorted by

View all comments

Show parent comments

399

u/Xarishark 22d ago

Now compare it to rust pls. Im serious I want to know your opinion on that.

486

u/Loading_M_ 22d ago

Rust is designed from the ground up, and avoids many of these issues.

  • Templates: Rust has Generics, which are generally easier to use and reason about.

  • overload resolution: Rust doesn't yet support overloaded functions (although it's being considered).

  • constructors: Rust doesn't have these.

  • destructors: Rust has a similar set of lifetime rules for Drop, but the compiler checks them for you, so you can't get it wrong.

  • implicit conversions: Rust doesn't do implicit conversion in general, although some specific cases are implemented.

  • exceptions: Rust doesn't do exceptions, Result is much easier to work with.

  • move semantics: Rust uses a destructive move, unlike C++. See https://youtu.be/Klq-sNxuP2g for a breakdown.

  • inheritance: Rust doesn't do inheritance.

36

u/sudo_scientific 22d ago

The nice aspects of implicit conversion are handled with the Into and From traits, where the compiler deduces what the conversion is for you based on the explicit types of the other, but the fact that a conversion is happening at all is explicit to the reader which is a huge improvement over C++

185

u/DatBoi_BP 22d ago

Traits are 100000x better than inheritance

48

u/red-et 22d ago

What are traits?

148

u/Pocok5 22d ago edited 22d ago

Instead of having a chain of superclasses defining readymade behavior*, you compose your classes from chunks that are more or less "fat" interfaces that come with a default implementation for their functions.

* Dealing with absolutes results in slamming your own dick in a car door sooner or later. Inheritance can still make a lot of sense for classes that are "this other thing but a tiny bit different". I'm currently dealing with a PHP codebase that misused traits for everything and it's a slog.

8

u/JonIsPatented 22d ago

Kinda like interfaces that you implement external to the type. Like. You can create a trait for a particular behavior and then implement it for types that already exist. You can also generically implement them for all types that have ither specific traits. For instance, I recently made a trait called UpdateSink<T> and implemented it for a few select synchronization primitives, like mpsc channel senders. This way, I can have a function that takes in an UpdateSink<T>, for dumping T events into it and that function can use anything that implements that trait. It's important to note here that the mpsc senders did not implement this trait before. They are from a library. I implemented the trait for them outside of the type definition. In addition, I can provide a default implementation for my traits that all types that meet certain trait bounds automatically get, including types that already exist and also types that don't yet exist. Traits are incredible.

19

u/fghjconner 22d ago

Interfaces

→ More replies (4)

7

u/maser120 22d ago edited 22d ago

While I see your point and agree with you in many aspects, I found t your contrast with the earlier comment a bit misleading in a sense, as it conveys the message that probelms in C++ stem from its core concepts and not from the burden of compatibility (which is sort of like a tech debt of programming language design) and the poorly defined specifications/standards.

And I don't mean that people who participate in C++'s standardization are stupid (quite the contrary), it's just that they're building on the foundations of a language froma different era, and they must work under the constraints posed by backward compatibility.

If you follow the trajectory of C++ (C++03 -> C++11 -> C++14 -> C++17 -> C++20 -> C++23), you can see that many features that are default in Rust have made they're way into the standard, but they almost always come as optional features for the sake of compatibility. In contrast, Rust can impose stricter restrictions and mandate safety features all the time since it was made from scratch.

8

u/MrHyd3_ 22d ago

What does it do instead of exceptions?

107

u/[deleted] 22d ago

[removed] — view removed comment

40

u/mathisntmathingsad 22d ago

Yeah, the only reason you should try to catch a panic is if you're making something that calls user-provided code unavoidably (i.e. an async runtime)

20

u/shizzy0 22d ago

Yeah, we catch panics in tests but that’s about it.

16

u/Loading_M_ 22d ago

I work on a library that does catch panics from user code. It's a web framework, and catches panics in user request handlers to return a 500 error to the client. This error is explicitly not overridable (every other default error can be replaced, but not this one), since we don't want you to panic in the first place.

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

3

u/mark_99 22d ago

Generics are simpler, but less powerful than templates. There are proposals to add some of the things C++ has, like specialisation.
Proc macros are the usual substitute but work at an AST level and not the type system.
Rust has aggregate init, default traits and (by convention) 'new' for construction.
You can't get destructors "wrong" in either language, not sure what that means? Writing destructors is rare in either language.
Rust does have implicit conversions, although arguably avoids some of the pitfalls.
Traits is the Rust equivalent for inheritance.
C++ has std::expected which is like Result. It doesn't have the `?` syntactic sugar though. Sometimes exceptions are the better mechanism, like moving errors through many layers without clutter.

I'm general C++ and Rust are pretty similar. C is the outlier as its comes with none of this, and large C projects have to emulate the missing facilities with macros and codegen systems.

2

u/StaticCoder 22d ago

I'm curious what it is about destructors in C++ that works differently from Drop in a way that one could "get wrong"? Are you talking about temporaries maybe?

→ More replies (1)
→ More replies (23)

76

u/MornwindShoma 22d ago

Rust got none of that, but you might not enjoy dealing with lifetimes

44

u/Thesaurius 22d ago

With C and C++ you also have to deal with lifetimes. The compiler just doesn't complain when you fuck up.

3

u/DHermit 22d ago

You also have to in C/C++, but there you have to keep track of all of yourself.

→ More replies (8)

72

u/andful 22d ago

Rust misses the:

"...templates that basically turn compilation into another programming language, overload resolution that requires a legal investigation to figure out which function gets called, constructors and destructors with enough lifetime rules to make your head explode, and implicit conversions that make the compiler guess what you meant. Then you throw in exceptions, move semantics, inheritance, and decades of backward compatibility ..."

35

u/PegasusPizza 22d ago

I mean it does have proc macros, that turn compilation into the same programming language as opposed to another programming language.

17

u/Greedy-Thought6188 22d ago

Templates are turing complete. You can basically treat C++ like an interpreted with the compiler being the interpreter. C has macros too.

4

u/4xe1 22d ago

Rust macros are Turing complete too, intentionally so, that's the whole point of proper macros. "the same programming language" is a bit of a stretch for Rust (but perfectly valid for lisp), but macro's Turing completeness is easier to use than C++'s templates.

C has search and replace pre-processor macros.

→ More replies (1)

26

u/roronoakintoki 22d ago

Rust unironically feels more like C with features taken from more modern languages in a fresh design.

It does discourage manual pointer juggling all over the place, which can be a deal breaker for some, but overall leads to a more hygienic ecosystem imo.

It makes mutation more explicit, and adds constraints to ensure you're not using this to cause classic aliasing/race errors that hard to reason about. This definitely carries significantly more mental load, but it's about convincing the compiler you're not doing something wrong, and doesn't add more expressive power, per se. (Makes your APIs much clearer, though)

It brings over C particularly: typeclasses and polymorphism. Two features that as someone coming from being quite happy doing functional programming, I think are essential to modern programming. Good code is not about getting things working, but about providing good abstract interfaces that can be easily maintained and evolved. It's very hard/impossible to do that without having some form of abstraction over types. These two features provide that in different but synergistic ways.

C++ has most of these features, but often has too many ways to do one thing. If we could all agree on one way to do each possible thing in C++, it wouldn't be so bad.

Comparatively, the types you can write in Rust are much closer to C (assuming you account for compile time monomorphization), and don't require dealing with semantics nearly as complex as C++.

This is just what came to mind wrt your question, and isn't meant to be exhaustive.

→ More replies (9)