I think it's useful to highlight that this is a much stronger type of immutability than e.g. Java's final, or equivalents in many languages.
If in Java we make a list:
final var list = new ArrayList<Integer>();
we cannot reassign it:
list = new ArrayList<Integer>();
but we can change it just fine in other ways:
list.add(1);
list.add(2);
list.clear();
Rust's Vec is a quite similar datatype Java's ArrayList:
let list = Vec::new();
but we cannot change it at all:
list.push(1);
list.push(2);
list.clear();
gives:
error[E0596]: cannot borrow `list` as mutable, as it is not declared as mutable
We might say that Java (and most others) has shallow immutability, while Rust's is deep.
There are some caveats though, mostly RefCell, Mutex and RwLock.
A tricky part about Rust's immutability is this:
fn f() { let list = Vec::new(); g(list); } fn g(mut list: Vec<i32>) { list.push(1); list = Vec::new(); }We're changing the list that was immutable.
Maybe this seems weird for several reasons: 1) wasn't the list an immutable object? 2) why doesn't that work with borrows? 3) why does Rust let me change immutable to mutable?
The first thing to remember is that, just like other languages such as Java, it's the reference that is immutable, not the object itself. Just, unlike Java, this also applies recursively to anything within the reference.
Another thing to remember is that, if you pass an object to another method, you're either passing sole ownership, or you're sending a copy.
If function
fpasses sole ownership of thelisttog, thenfcannot have any references anymore. Whatevergdoes, it won't affectf. So it's no problem ifgmakes it mutable.And if
fpasses a copy, of course it won't care whatgdoes with that; it still has it's own version.