Why does Rust allow upgrading/downgrading mutability?

Viewed 240

In rust, you can upgrade an immutable variable:

let v = Vec::new(); // Immutable
let mut v = v;      // Mutable
v.push("hi");       // Succeeds

Or downgrade a mutable variable:

let mut v = Vec::new();   // Mutable
let v = v;                // Immutable
v.push("hi");             // Won't compile

My question is - why?

From what I understand, the underlying memory in use to store values of variables is never immutable. Every memory address can technically be written to. Immutability is an artificial constraint, that someone sets on us (like the kernel), or we set on ourselves.

When I say:

let v = vec!["a", "b", "c"];

I'm saying that I want a variable in memory with these values, and I don't want it changed by any code later on. If I try to change this variable at some point, give me an error. This is me defining the constraint.

If you can later just do:

let mut v = v;

making it mutable, and change it, this seems to defeat the entire purpose of immutable variables. At that point, you might as well just make all variables mutable (like in Python), because immutability is not guaranteed. Not only is it not guaranteed, but a programmer can get a false sense of the guarantee of immutability, and make mistakes based on this assumption.

At least with constants, there's a guarantee of immutability. The purpose of a regular immutable variable is unclear if you can just make it mutable at any point.

2 Answers

In rust, you can upgrade an immutable variable:

let v = Vec::new(); // Immutable
let mut v = v;      // Mutable
v.push("hi");       // Succeeds

This isn't upgrading a variable. You are creating a variable v and moving it into another variable also called v. The original v is no longer accessible. In debug mode the generated code likely has two completely different (stack) memory addresses for the first and second v.

As for downgrading the same applies.

this seems to defeat the entire purpose of immutable variables.

Once you have moved a value you can no longer use it, and you cannot move a value if there are references to that value in existence. Therefore you never have a situation where you have a mutable reference to something that is now immutable.

Rust allows for redefinition of variables. So in your example you declare a new variable v and copy the vector into it :

let v = Vec::new(); // Immutable
let mut v = v;      // New Mutable <—-
v.push("hi");       // Succeeds

Because the vector implements the copy trait.

You can make your sample (not) work as you intended by borrowing as reference:

let v = Vec::new(); // Immutable
let &mut v = v;      // Doesnt compile
v.push("hi");
Related