What does mut really mean

Having read the first chapters of The Book, I'm still somewhat confused about the meaning of mut when applied to a variable, like in:
let mut x = 5;
let mut v = Vec::new();
My current understanding is, that mut has two effects:

  1. It allows to assign another value to the variable later on.
  2. It allows to modify the object that the variable refers to later on.

If I want to do either of this two things, I have to use mut.

I am wondering if this understanding is correct, because actually this two things have quite different semantics in my opinion. Are they really covered by one keyword, and what is the rationale behind this ?

4 Likes

A third way of changing a variable would be shadowing.

let a =5;
let a = 8; 

I'm in the middle of writing about this, since the question has more depth than it seems, specially when you take into account interior mutability.

Short answer:

  • when the variable/binding owns some data (v: Vec<i32>), you need to use let mut v when declaring that binding to later be able to change that data, either by value (v = vec![]) or by mutable reference (v.clear()).
  • when the variable points to some data without owning it, then:
    • either that pointer is statically proved to be unique (called a mutable reference), in which case you can mutate the pointee regardless of the mut-ness of the binding "holding" the &mut _ (declaring the &mut _ binding mut will allow you mutation-by-value, i.e., to change to which data the unique reference points to);
    • or the pointee exposes an API with Interior Mutability (e.g., UnsafeCell, Cell, RefCell, Mutex, RwLock) that allows you to mutate through a (potentially) shared reference. In that case the mut-ness of the binding "holding" the & _ again plays no role regarding mutating the pointee, it allows just to change the pointer.

Some remarks

  • Since mut is just a property of the binding and not of the "data", you can rebind in and out of mut-ness.
    Example:

    let v = {
      // in this scope v is mut
      let mut v = Vec::with_capacity(2);
      v.push(42);
      v.push(27);
      v
    };
    // v is now "not mut"
    let n: i32 = v.into_iter().sum();
    dbg!(&n);
    
  • anonymous bindings (i.e., owned data that has not been explicitely assigned to a variable) are always mut:

    fn emphasize_and_print (s: &mut String)
    {
      s.push_str("!!");
      println!("{}", s);
    }
    
    fn main ()
    {
      // `String::new()` is anonymously bound
      emphasize_and_print(&mut String::new());
    
      let s = String::
        from("Hello, World")
      ;
      // `s` is moved out of the tiny scope,
      // thus becoming anonymous and mut
      emphasize_and_print(&mut {s});
    }
    

Shadowing does not change a variable, it introduces a new one with a new name.

3 Likes

Yes of course, I hope I did not suggest the opposite

@Yandros That was meant for me

1 Like
  1. you can modify all the bits.
  2. you can modify some of the bits.

Stated is this over-simplistic way they are the same.

The rationale is that the alternative is hideous. As demonstrated by JavaScript.

Well, you can't ever "lock" a part of a struct, so really what should be said is that you are either replacing the existing data, or providing a mutable reference to it to a function which then mutates the data (Instance methods' &mut self parameters)

Your thinking is correct.

mut is just a lint for the programmer, because when you own a value you can mutate it anyway.

OTOH &mut is a part of the type system and is always enforced strictly.

4 Likes

Here's a way of looking at it that I think makes 1 and 2 appear the same:

When you write let mut x = 5, the compiler puts 5 in a stack slot, and associates that slot with the name x.

Because you wrote mut, the compiler will let you change the bits in that stack slot.

So all the ways of modifying x in main() below are doing the same sort of thing:

trait AddOne {
    fn add_one(&mut self);
}

impl AddOne for i32 {
    fn add_one(&mut self) {
        *self += 1;
    }
}

fn main() {
    let mut x = 5;
    x = 6;
    x = x + 1;
    x += 1;
    x.add_one();
}

And this is more than an implementation detail.

let mut a =5;
a = 6;

and

let a = 5;
let a = 6;

are not equivalent from the perspective of subsequent code in the same lexical scope. In the former, 'a' can be mutated by that code; in the latter, it can't.

This way of looking at it makes really sense for me, many thanks for pointing that out !

You can have shadowing yield a mutable variable:

let a = 5;
let mut a = 6;

and the other way around:

let mut a = 5
a = 6;
let a = a;