DEV Community

Jazz Thumyat 🦀
Jazz Thumyat 🦀

Posted on

Understanding `&mut &'a self` Calls on Temporaries in Rust

[T] has the following split_off method. Notice that its receiver type is &mut &'a Self:

pub fn split_off<'a, R: OneSidedRange<usize>>(
    self: &mut &'a Self,
    range: R,
) -> Option<&'a Self> {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Now look at this code:

let v = vec![1, 2, 3, 4, 5];
let head = v.as_slice().split_off(..2);

assert_eq!(head, Some([1, 2].as_slice()));
Enter fullscreen mode Exit fullscreen mode

This may look confusing at first.

v.as_slice() returns &[i32], but split_off() expects &mut &[i32]. So where does the mutable reference come from?

The answer is that v.as_slice() creates a temporary value. In Rust, a temporary is a mutable place, so the compiler can automatically borrow it as &mut.

You can think of the code as if the compiler had written this:

let mut temp = v.as_slice();
temp.split_off(..2);
Enter fullscreen mode Exit fullscreen mode

The mutable reference is taken to the temporary variable (temp), not to the original vector or slice. The vector v and the slice it produces are never mutated. Only the temporary variable is.

Proof that temporaries are mutable places

A normal let binding is immutable unless you write mut. Because of that, the following code does not compile:

let s: &[i32] = v.as_slice(); // no `mut`
let _ = s.split_off(..2);     // compiler error here
Enter fullscreen mode Exit fullscreen mode

The compiler reports:

error[E0596]: cannot borrow `s` as mutable, as it is not declared as mutable
13 |     let _ = s.split_off(..2);
   |             ^ cannot borrow as mutable
help: consider changing this to be mutable
12 |     let mut s: &[i32] = v.as_slice();
Enter fullscreen mode Exit fullscreen mode

This shows the difference:

  • let s: &[i32] = v.as_slice() creates an immutable place, so Rust cannot take &mut s.
  • v.as_slice() creates a temporary, and a temporary is a mutable place, so Rust can automatically take &mut to call split_off().

That is why v.as_slice().split_off(..2) compiles, while s.split_off(..2) does not unless s is declared with mut.

Top comments (0)