Mutability

From Rust Community Wiki

Mutability is an effect that allows modifying a value, which is enforced by Rusts type system and guarantees the absence of data races. The name "mutability" is problematic, because types with interior mutability can be changed even when the value is immutable.

Mutability of owned values

By default, owned values are immutable, which means that they can't be modified:

// x is immutable:
let x = 5;
x = 6;

error[E0384]: cannot assign twice to immutable variable `x` | 2 | let x = 5; | - | | | first assignment to `x` | help: make this binding mutable: `mut x` 3 | x = 6; | ^^^^^ cannot assign twice to immutable variable

// y is mutable:
let mut y = 5;
y = 6;
y += 1;


Only mutable variables, declared with the mut keyword, can be reassigned. To make an immutable variable mutable, it must be moved to a new variable:

let x = 5;
let mut y = x;
y *= 2;


It is possible to shadow a variable name to achieve something similar to mutability:

let x = 5;
let x = x * 2;


Note that this declares two distinct variables which just happen to have the same name. With shadowing, it is not possible to change the value of an immutable variable. The difference is demonstrated by the below example, which shows how shadowing interacts with block scope:

let x = 1;
if true {
    let x = 2;
}
assert_eq!(x, 1); // x was not changed, just shadowed

let mut y = 1;
if true {
    y = 2;
}
assert_eq!(y, 2); // y was actually mutated


Function parameters and other bindings

let bindings are patterns, as are function parameters. The mut keyword is allowed in all patterns where a new binding is introduced, for example:

// destructure a tuple:
let (mut x, mut y) = (1, 2);

// match on an array:
let array = [1, 2, 3];
match array {
    [mut x, _, _] => {}
}

// bind a parameter mutably:
fn foo(mut a: i32) {}


Note that a variable doesn't need to be mutable in order to pass it to a function or match block where it is bound mutably, because the variable is moved in that case.

Mutability of borrowed values

When borrowing a value, it can be borrowed either mutably or immutably. This is done by creating a mutable or immutable reference:

let x = 42;
// create three references to x:
let ref1 = &x;
let ref2 = &x;
let ref3 = &*ref1;

let mut y = 42;
// create a mutable reference to y:
let mut_ref = &mut y;


While you can create as many immutable reference to a value as you want, mutable references must be exclusive: At any point, there can be at most one mutable reference to a value. Furthermore, while a mutable reference exists, no immutable reference may exist. This ensures that a variable can't be accessed while it is being modified by someone else. Example:

fn foo(mut x: i32) -> i32 {
    let ref1 = &mut x;
    *ref1 += 3;
    
    // a mutable reference already exists
    // but that's okay, since it isn't used after line 3
    let ref2 = &x;
    x += 4; // ERROR!
    *ref2
}

error[E0503]: cannot use `x` because it was mutably borrowed | 7 | let ref2 = &x; | -- borrow of `x` occurs here 8 | x += 4; // ERROR! | ^^^^^^ use of borrowed `x` 9 | *ref2 | ----- borrow later used here


This doesn't work because the value of x is mutably borrowed twice, by both x and ref2.

Shared and exclusive references

The term "mutability" is problematic, because a value behind an immutable reference can be mutated, if its type has interior mutability (see below). Therefore some people recommend to call immutable references "shared references" instead; mutable references should be called "exclusive references". This is less ambiguous and more in line with Rusts memory model: When a reference is exclusive, it has exclusive access to the data it refers to, even in the presence of interior mutability.

Mutating functions

It's possible to mutate a value in a function through a mutable reference, for example:

#[derive(Debug)]
struct Point(i32, i32);

impl Point {
    fn inv(&mut self) {
        self.0 = -self.0;
        self.1 = -self.1;
    }
}

let mut p = Point(1, 4);
p.inv();
println!("{:?}", p); // prints Point(-1, -4)


Note that p.inv() is equivalent to Point::inv(&mut p). If p isn't mutable, it can't be mutably borrowed, so its inv() method can't be called.

Interior mutability

See also: Reference – Interior mutability

It is possible to create types that can be mutated even if they're behind an immutable reference. This is done with the UnsafeCellThis links to official Rust documentation type. As the name already suggests, its usage is unsafe. However, it is possible to create safe abstractions on top of it. For example, the standard library contains the CellThis links to official Rust documentation, RefCellThis links to official Rust documentation, MutexThis links to official Rust documentation and RwLockThis links to official Rust documentation types, as well as atomicThis links to official Rust documentation types. These use UnsafeCell internally, but perform runtime checks to ensure the absence of data races. This is only necessary if Rust is unable to guarantee the absence of data races at compile time.

Cell

The Cell type is the simplest type that allows interior mutability; it allows moving an new value into the Cell. However, it doesn't allow taking a reference to the inner value, and isn't thread safe.

let cell = Cell::new(5);
cell.set(6);
cell.set(cell.get() * 2);


RefCell

The RefCell type is a more powerful version of Cell, since it allows obtaining a mutable reference to the inner value, or multiple immutable references. Like Cell, it isn't thread safe.

let ref_cell = RefCell::new(String::from("Hello, world"));
{
    let mut s: RefMut<String> = ref_cell.borrow_mut();
    s.push('!');
}
println!("{:?}", ref_cell);


RefCell uses the guard pattern to check when the inner value is borrowed: The borrow_mut() method checks if the RefCell is already borrowed mutably and panics if that is the case. Then it increments a counter and returns a RefMut guard that acts as a mutable reference. When this guard goes out of scope, the counter is decremented again.

Mutex

A thread-safe version of RefCell. Note that the inner value can't be borrowed immutably multiple times. To do that, use RwLock. Bear in mind that locking a Mutex or RwLock can deadlock.

let mutex = Mutex::new(String::from("Hello, world"));
{
    let mut s: MutexGuard<String> = mutex.lock().unwrap();
    s.push('!');
}
println!("{:?}", mutex);


Like RefCell, it uses the guard pattern. Note that lock() returns a ResultThis links to official Rust documentation, in case the mutex was poisoned. It's usually okay to just unwrap the result.

RwLock

A read-write-lock. It is the most powerful, thread-safe type for interior mutability in the standard library. It can be mutably borrowed once or immutably borrowed multiple times.
let rw_lock = RwLock::new(String::from("Hello, world"));
{
    let mut s: RwLockWriteGuard<String> = rw_lock.write().unwrap();
    s.push('!');
}
println!("{:?}", rw_lock);
This is similar to Mutex, except that it has the read() and write() methods instead of lock().

Atomic* types

The standard library contains several wrappers around primitive types, such as AtomicBoolThis links to official Rust documentation, AtomicI32This links to official Rust documentation, AtomicUsizeThis links to official Rust documentation, etc. These allow accessing and mutating values shared across multiple threads. The underlying value can only be modified via their methods, which are atomic. There are five different strategies how these types can synchronize memory; these are enumerated in the OrderingThis links to official Rust documentation struct.
let b = AtomicBool::new(true);
assert_eq!(true, b.load(Ordering::Relaxed));

b.store(false, Ordering::Release);
assert_eq!(false, b.load(Ordering::Acquire));

Summary

Type allows borrowing
the inner value
allows multiple
immutable borrows
thread safe
Cell no no no
RefCell yes yes no
Atomic* types no no yes
Mutex yes no yes
RwLock yes yes yes

Mutable static variables

Main article: Static variables

Static variables can be mutable. Since Rust can't prove the absence of data races of global mutable variables, accessing a static mutable variable is unsafe.