As others have said, the core idea here is to take a &mut &... [T]
(where ...
is mut
or empty) and read/write to the internal slice. The other answers demonstrate it is possible for &mut &[T]
in safe code, and possible for &mut &mut [T]
with unsafe code, but they don't explain why there's the difference... and &mut &mut [T]
is possible with safe code too.
In explicit-lifetime terms, the nested reference is something like &'a mut &'b ... [T]
for some lifetimes 'a
and 'b
, and the goal here is to get a &'b ... [T]
, slice it and write that into the &'a mut
.
For &'a mut &'b [T]
, this is easy: &[T]
is copy, so writing *slice = &slice[1..]
will effectively copy the &'b [T]
out of the &mut
and then, later, overwrite the existing value with the shorter one. The copy means that one literally gets a &'b [T]
to operate with, and so there's no direct connection between that and the &'a mut
, and hence it is legal to mutate. It is effectively something like
fn pop_front<'a, 'b>(slice: &'a mut &'b[i32]) {
// *slice = &slice[1..] behaves like
let value: &'b [i32] = *slice;
*slice = &value[1..]
}
(I've labelled the lifetimes and annotated the type to tie into my explanation, but this is not required for the code to work.)
For &'a mut &'b mut [T]
things are a little trickier: &mut [T]
cannot be copied: dereferencing won't copy, it will reborrow to give a &'a mut [T]
i.e. the slice has a lifetime that is connected to the outer &'a mut
, not the inner &'b mut [T]
. This means the sliced reference has a shorter lifetime than the type it is trying to overwrite, so it's invalid to store the slice into that position. In other words:
fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) {
let value: &'a mut [i32] = &mut **slice;
*slice = &mut value[1..] // error
}
The way to do this safely for &'a mut &'b mut [T]
is to get the internal slice out of the reference with that 'b
lifetime. This requires keeping track of the "one owner" rule, doing no borrowing, and the best function for this sort of ownership manipulation is mem::replace
. It allows us to extract the inner &'b mut [T]
by swapping it with some placeholder, which we can then overwrite with the short version. The best/only placeholder is an empty array: writing &mut []
can be a &'c mut [X]
for any type X
and any lifetime 'c
, since there's no data to store and so nothing needs initialisation, and no data will ever become invalid. In particular, it can be a &'b mut [T]
:
fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) {
let value: &'b mut [i32] = mem::replace(slice, &mut []);
*slice = &mut value[1..]
}
Since &mut[T]
implements Default
, we can also use mem::take
:
fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) {
let value: &'b mut [i32] = mem::take(slice);
*slice = &mut value[1..]
}
(As above, I've made things more explicit than necessary.)
See also: