|
Const in C++ is entirely unlike immutability in Rust. Even ignoring the existence of “const_cast”, C++ const is vastly weaker than Rust immutability in several extremely fundamental ways that should be immediately obvious if you’re a C++ professional with even a surface level introductory understanding of Rust. At this point you honestly should just take some time to learn a bit of Rust, and it will likely all make sense. If not, it’s possible you’re relatively amateur C++ developer (no shame in that) lacking experience of the many ways in which C++ const falls very short. But to directly answer your question, suppose I have a “struct FloatArrayView { float* data; size_t size; };” Of course this is a toy example, but humor me for now and consider the following: 1. Without excessive custom effort (e.g. without manually unwrapping/wrapping the internal pointer), how do I pass it to a function in C++ such that the data pointer within becomes const from the perspective of that function (i.e. you’ll get a compile error if you try to write to it?) Hint: It’s very complex to do this in C++, vs trivially easy in Rust. And no, passing as const or const reference to “FloatArrayView” does NOT work. The only solution in C++ for complex composite types operating with “by-reference semantics” is ugly, complex, and horribly error prone to maintain correctly. For a more concrete example, consider how “unique_ptr” works with the constness of the value it holds. A “const unique_ptr<T>” is NOT a unique pointer to const T. A “unique_ptr<const T>” is, but the relationship and safe conversions between these are not simple or easy to implement or even use in many cases. It gets even worse when you need to implement a “reference semantics” type like this that is inappropriate to be a templated type, or contains a variety of internal references unrelated to the template arguments. 2. Now suppose I solve #1 and pass this into some class method, which then stores a copy of the const reference for later. But I as the caller have no way of knowing this. So after that method returns, I later proceed to modify the data via my non-const reference (which is a completely valid operation), but this violates the previous method’s assumption that the data was immutable (will never change). This creates an incredibly dangerous situation where later reads from that stored const reference to data (that was assumed to be immutable) is actually going to actually yield unpredictably changing results. Const is not immutable. Question: How do you make it so C++ guarantees that passed reference is truly immutable and will never be mutated so long as the reference exists, and enforce this guarantee at compile time? In Rust this is easy (in fact, it’s the default). In C++, it is impossible. The closest you can get in C++ are runtime checks, but that’s nowhere near as good as compile time checks. Edit: Removed a bunch of perhaps unnecessary extra C++ trivia which I’ll save for later :) |
To be more clear, in Rust, the `mut` you see in bindings (like `let mut blah = ...`) is wildly different from the `mut` in `&mut T`. There was discussion on whether the latter should be renamed to `uniq` (because `mut` is misleading). In any event, Rust lacks `const` types. There is no `const T` like in C++. So something like this is impossible,
You can't have just the elements of a collection to be immutable like the above example in Rust.Secondly, a value of a type `&T` can be mutated internally (aka interior mutability). A function taking a `&T` type and mutating it behind the scenes is very whacky. AFAII, interior mutability exists only to trick the borrow checker using the `*Cell` types (which use unsafe internally BTW).