[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> {
// ...
}
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()));
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);
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
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();
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&mutto callsplit_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)