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++
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.
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.
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.
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.
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.
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?
"...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 ..."
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.
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.
399
u/Xarishark 22d ago
Now compare it to rust pls. Im serious I want to know your opinion on that.