The author clearly knows this, but others may not, so: immutability and "const" aren't the same thing. Const typically prevents name rebinding (as the type system permits). Immutability is the presumption that the whole data structure can't be mutated by default (though there are usually ways to mutate cheaply--without bulk copying, that is--provided by most immutable data APIs).
Const works like this:
not_const int[] foo = [123, 456]
const int[] bar = [456,789]
foo = [1,2,3] // no problem; rebinding is allowed if it meets the 'int' type constraint.
bar = [1,2,3] // compile error!
Immutability, as typically defined (there's a bit of a semantic debate here sometimes) prevents changes to the contents of complex data structures:
mut int[] foo = [123, 456]
not_mut int[] bar = [456,789]
foo[0] = 789 // no problem; mutation is allowed so long as it's with valid types.
bar[0] = 123 // compile error!
Const typically prevents name rebinding (as the type system permits).
In C++ you can only call `const` methods through a const binding. `const` methods mark `this` const, so such methods cannot modify member variables. (Minus all escape hatches, such as `const_cast`.)
The author clearly knows this, [...] Immutability is the presumption that the whole data structure can't be mutated by default
The situation in Rust is a bit more complex. Rust distinguishes between interior and exterior mutability.
`let mut` bindings and `&mut` references exhibit exterior mutability - you can call methods that borrow `self` mutably (`&mut self`). Whereas `let` bindings and `&` references cannot. By the way, you can simply use moves to introduce exterior mutability for data that you own:
let a = Vec::new();
// Not allowed: a.push(1);
// Move the vector.
let mut b = a;
b.push(1);
Even even if a data type uses an immutable binding or reference, it could still mutate that data structure if it has interior mutability (you just can't call methods that use self mutably). See the following small example, which uses an immutable binding to call a method that mutates the internal state of the struct.
It's interesting that Rust doesn't have immutable data. It "only" prevents shared mutable data, but you can always mutate data if nobody else can observe it:
let bar = [456, 789]; // const binding to owned data
let mut bar = bar; // allowed if nobody else can see `bar`
bar[0] = 123; // OK!
Immutability in Rust is not property of the data, but the way you access it.
This explanation of const sounds much like Java's final, but not like C++'s const guarantees, which enable you to guarantee nobody downstream of a const modified reference will be allowed to perform a write operation through that reference.
You cannot get around that behavior by assigning the reference to a non-const copy. That isn't allowed.
But if some other code gets a non-const reference to an object via another pointer path, that code can mutate the object, even though const pointers to that object may exist. In Rust, this is not the case.
That difference severely weakens the meaning of the const property in C++.
No. C++ const just means "I won't mutate this object" (through the const pointer). It doesn't mean that somebody else won't mutate the some object (through their non-const pointer to the same object).
struct X {
X(Y val) : y_{val} {}
// Can be called on const and non-const X objects
const Y& y() const { return y_; }
// Can only be called on non-const X objects
Y& y() { return y_; }
private:
Y y_;
};
void f(X& x) { x.y() = 222; }
void g(const X& x) { x.y() = 222); }
const X x{123}; // x.y() == 123
x.y() = 222; // compilation error
f(x); // compilation error (f's parm is non-const)
g(x); // compilation error (can't assign to const ref)
// Cannot take non-const ref or ptr of x
X& xr{x}; // compilation error
X* xp{&x}; // compilation error
If the original declaration of the variable is const nothing can modify it. (And as my first example shows, the original declaration can always be const if you want it to be, regardless of context.) You can't take a non-const pointer or reference to x without explicitly circumventing the language like this:
Sure, if the object was declared const in the first place, then it (mostly) can't be mutated. But the real power of const comes from const references, which in Rust guarantee that the referenced object will not be mutated while the reference is alive, even though it might have been mutated before.
You're thinking of a const pointer or reference. Of course if the data the pointer is pointing at changes, if you dereference it you get something else. But notice: the pointer, ie. the page number of your book, or the offset in your array, did indeed remain const. It is just the contents of the book/array that changed.
So, a const value cannot be mutated locally or globally. So if you make something const and expose it, it cannot be changed.
> Immutability (const) by default
The author clearly knows this, but others may not, so: immutability and "const" aren't the same thing. Const typically prevents name rebinding (as the type system permits). Immutability is the presumption that the whole data structure can't be mutated by default (though there are usually ways to mutate cheaply--without bulk copying, that is--provided by most immutable data APIs).
Const works like this:
Immutability, as typically defined (there's a bit of a semantic debate here sometimes) prevents changes to the contents of complex data structures: Edit: missed a space.