3 ms·
Many of the criticisms are of the C++ standard library design and implementation rather than C++ the language per se, particularly with respect to undefined beh
by jandrewrogers 2y ago
Many of the criticisms are of the C++ standard library design and implementation rather than C++ the language per se, particularly with respect to undefined behavior. Much of this, in turn, is because the C++ standard library is very old and anything new added to it must be compatible and interoperable with all of the older parts, whether or not it is a good idea. Borrow checking is a separate matter.
Modern C++ provides all the tools to build an alternative standard library from the ground up with most behaviors and interactions being defined and safe. This always seemed like lower-hanging fruit for a first attempt than changing the language.
C++ is commonly used in contexts where object lifetimes and ownership are inherently unknowable at compile-time, safety can only be determined at runtime. This is why memory safety annotations like 'std::launder' exist in C++. Trying force these cases into compile-time memory safety box is a source of much friction when using languages like Rust. They handle the 80% case where compile-time safety, destructive moves, etc make things easy for the developer but then significantly worsen the complexity, and therefore safety, of the other 20%. This isn't necessarily a bad thing but it intrinsically creates a market for C++ which explicitly allows you to handle these cases in a reasonable way.
Systems programming is full of edge cases that break tidy programming language models. This has to be accommodated reasonably in languages that want to wear the mantle of "systems language". Zig is an example of a new systems language that does this well in my opinion.
- pjmlp 2y agoZig is basically Modula-2 with a revamped syntax for C folks, plus compile time metaprogramming. And yes, many of these issues were already solved by other system programming languages outside the UNIX linage of programming languages.
- 112233 2y ago> Modern C++ provides all the tools to build an alternative standard library from the ground up with most behaviors and interactions being defined and safe. It also goes to a great lenght to embed standard library into base language as deeply as it can, and forces unsafe constructs. Just see how many magic types from std namespace are emmited by compiler when you use range for, initializers, or, worst of all, coroutines. Lambdas with capture by reference replace old fashioned dangling pointers with much more modern dangling references (you are free to copy lambdas that reference local variables :smiling virus with thumbs-up emoji:). There is no way to get "another library", without making it not-c++. Those guys cannot even make no-exceptions/no-rtti a part of the standard.
- Gibbon1 2y agoThe dependence of C++ is one of the reasons I think C and C++ need a divorce. Because while you'll never be able to create a new standard lib for C++ it wouldn't be hard for C.
- 112233 2y agoIt's more like C++ made by that ISO committee and C++ used by actual users of C++ needs a divorce. They are so far up in the clouds adding features to language that mostly only help them to write less embarrasing standard library code, it hurts to watch sometimes. Remember discussions that led to std::bit_cast? How absolutely everyone who processes binary data is wrong, because object of type, say, "unsigned" does not exist before it is created, so everyone casting pointers to parse packets is wrong, and only commitee is right? It is doubly sad to see them claim compatibility to C (C++ is absolutely not compatible to C) as a reason for some features, or single pass compilation as a reason for others... [edit] To clarify, on one hand, a lot of terrible misfeatures are kept in the name of compatibility, e.g. "int x = x;" ("fixing that will break too many existing exploits") or not being able to add friends without editing source ("debugging? we think it is overrated"), but then they go and proclaim basically all existing code that processes structured binary data as non-conformant.
- nextaccountic 2y ago> a lot of terrible misfeatures are kept in the name of compatibility, e.g. "int x = x;" ("fixing that will break too many existing exploits") What does this code do? Is this referring to a previously defined x, now shadowed by a new x? Like this int x = 1; int x = x; If yes, Rust has this feature too. And in Rust, shadowing variables is very idiomatic and is sometimes used to create safe abstractions.
- 112233 2y agoNo, it is referring to the x that is being initialized, and uses uninitialized memory to initialize x. -Wall will even print a helpful reminder. Compare: int f(int x) { int x = x; // second x is not argument return x; } and: int f(int x){ // here second x is argument return [x=x] { return x; }(); }
- mike_hearn 2y agoSee for example https://suslib.cc/ https://suslib.cc/
- pornel 2y agoI think the "unknowable" lifetimes are C++'s own making. It's like types in statically vs dynamically typed languages. Types are unknowable at compile time when the compiler doesn't force them to be static. And similarly ownership and lifetimes are "dynamically typed" in C++, because the compiler doesn't force them to be rigidly declared like in Rust.
- kccqzy 2y agoUnknowable lifetimes are a necessary evil. Rust has them too. It's called RefCell. Without RefCell there are very useful things that are impossible to write in safe Rust.
- pornel 2y agoBut this needs to be explicit, and doesn't undermine other statically-known lifetimes (pointers to the inner cell can't escape it). Just like statically typed languages have some dynamic dispatch features, but despite that still most of their types can be known with certainty.
- orf 2y ago> C++ is commonly used in contexts where object lifetimes and ownership are inherently unknowable at compile-time What specific contexts are these?
- jandrewrogers 2y agoFor example, anything that pages live objects to disk from user space. This is a common feature of high-performance/high-scale database engines and other data intensive software. Modern storage is larger than the addressable virtual memory supported by silicon so you can't mmap() it, and even if you could you wouldn't want to because using virtual memory is significantly slower than doing it from user space. A single memory address can contain several unrelated live objects depending on when you look; a single live object can appear at different memory addresses over its lifetime without being moved; and live objects may not have a memory address at all. The hardware implicitly owns read and write references to these objects, which are not visible to the compiler and free to ignore the ownership model. Consequently, you need a way to tell the compiler that the object it thinks exists at an address no longer does, and that object has neither been moved nor destroyed, and that there may be an unrelated object at that address that was never constructed. In these cases, compiler optimizations can generate incorrect code based on a naive analysis based on what is visible at compile-time. C++ provides ways to give this a defined behavior and indicate to the compiler when its lifetime model is incomplete so that it doesn't produce incorrect code. You can build ownership models on top of this that incorporate ownership that is not visible at compile-time. Some C++ devs don't know these features exist, other C++ devs use them routinely. But the point is that C++ has specific features to address the case when lifetime analysis cannot be correctly done by the compiler.
- pcwalton 2y agoUserspace-paged data is a tiny fraction of the amount of data that C++ programs, taken as a whole, process, and is a very weak argument for dropping static memory safety. Besides, because of unsafe, any Rust-like language, like Circle C++, will have the features needed to deal with such data too.
- nextaccountic 2y ago
- roca 2y agoThe claim that Rust makes the safety of 20% of code worse requires some justification. It is utterly contradictory to my experience. Having to do a dynamic check (an array bounds check, or `Option::unwrap`) does not make code "less safe".
- charleslmunger 2y agoThis post makes a cogent argument that Rust's rules that are normally enforced by the compiler makes unsafe rust harder to write without UB than other languages that are not memory safe by default. https://zackoverflow.dev/writing/unsafe-rust-vs-zig/ https://zackoverflow.dev/writing/unsafe-rust-vs-zig/
- roca 2y agoIn my Rust projects (including Pernosco, 200K lines), less than 0.1% of code directly uses "unsafe". It's a long way from there to 20%.
- pcwalton 2y ago> They handle the 80% case where compile-time safety, destructive moves, etc make things easy for the developer but then significantly worsen the complexity, and therefore safety, of the other 20%. Thankfully we have actual data on this, not just random numbers, and no, this is not the case in practice.
- geertj 2y ago> Thankfully we have actual data on this, not just random numbers, and no, this is not the case in practice. Mind to share that data? I think it would be hard to get quantitative data either way.
- pcwalton 2y ago70% of high severity security problems in Chrome are memory safety bugs: https://www.chromium.org/Home/chromium-security/memory-safety/ https://www.chromium.org/Home/chromium-security/memory-safet... You can find several papers about Rust in particular by searching for [rust memory safety analysis].
- geertj 2y agoI am familiar with that number but this does not address the point the top-level comment is making. The commenter claims is that there exists a class of problems where lifetime and ownership are not knowable at compile time, and makes the claim that for these problems Rust is worse than C++.
- pcwalton 2y agoWell, I don't know what "worse" means in this context. If it means "less memory safe", that's clearly not true: RefCell and Mutex are formally memory safe, while the C++ equivalents are not. If it means "less correct" in a more vague sense, then that's essentially an argument that Rust programs have more bugs than C++ programs, which seems dubious and certainly not something I'd accept at face value.