rustmutability

Trying to understand Mutability. Modify Reference vs. modify Object


I am trying to understand mutability.

When I declare a variable in Rust with

let mut a = String::from("Foo");

I can modify the Object, e.g.:

a.push_str("Bar");

Or assign a new Object, e.g:

a = String::from("Bar");

When I declare a variable without 'mut'

let a = String::from("Foo");

I can neither modify the Object nor assign a new Object.

So I can either choose to:

  1. have a mutable object with a mutable variable or
  2. have an immutable obejct with an immutable variable

I wonder if it is possible to declare vaiables so that

  1. the object is immutable, but the variable is mutable and can be assigned to a different object or
  2. the object is mutable, but the variable is immutable and cannot be assigned to a different object.

I read the chapter about mutability in the book but couln't find any information which could help me further.


Solution

  • First, we need to make an important distinction:

    In let mut, mut is a property of the binding, i.e. the variable. The variable and everything it contains can be mutated, but only as long as it contains it. We can transfer the content to another variable, and then mutability will be decided by the new one:

    let v = String::from("Foo");
    // Cannot mutate `v`.
    v.clear(); // ERROR
    v = String::new(); // ERROR
    
    let mut new_v = v;
    new_v.clear(); // OK
    new_v = String::new(); // OK
    

    In contrast, mutability of references (& and &mut) is a property of the value. Even if we transfer ownership, shared references cannot be mutated (unless they contain interior mutability).

    Now, I can answer your questions:

    the object is immutable, but the variable is mutable and can be assigned to a different object or

    Yes, if we make the variable a shared reference:

    let mut v = &String::from("Foo");
    v.clear(); // ERROR
    let other = String::new();
    v = &other; // OK
    

    the object is mutable, but the variable is immutable and cannot be assigned to a different object.

    Yes, if an immutable variable contains a mutable reference:

    let v = &mut String::from("Foo");
    v.clear(); // OK
    let mut other = String::new();
    v = &mut other; // ERROR
    

    Another way to satisfy (2), as already said, is using interior mutability:

    let v = std::cell::RefCell::new(String::from("Foo"));
    v.borrow_mut().clear(); // OK
    v = String::new(); // ERROR