How can I modify a slice that is a function parameter?

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:

  • Why can't I assign one dereference of a reference of a reference to another when the outer lifetimes differ?
  • Why does linking lifetimes matter only with mutable references?
  • How can I swap in a new value for a field in a mutable reference to a structure?

Using part of Francis Gagné's answer (I didn't think of trying &mut &), I was able to get it working without using unsafe code:

fn pop_front(mut slice: &mut &[i32]) {
    *slice = &slice[1..];
}

fn main() {
    let mut slice = &[0, 1, 2, 3][..];
    pop_front(&mut slice);
    println!("{:?}", slice);
}

Output:

[1, 2, 3]

If you need to modify an immutable slice, see Cornstalks's answer.

You cannot modify a mutable slice in safe Rust. When you take a subslice of a mutable slice, you effectively borrow from the original slice. This means that the subslice must not outlive the original slice.

You want something that looks like this:

fn pop_front(slice: &mut &mut [i32]) {
    *slice = &mut slice[1..];
}

but the subslice slice[1..] is only valid until the end of the function, and which point the borrow will end and the original slice (the slice parameter) will be usable again.

We can use some unsafe code to construct manually the slice we want:

use std::slice;

fn pop_front(slice: &mut &mut [i32]) {
    let ptr = slice.as_mut_ptr();
    let len = slice.len();
    *slice = unsafe { slice::from_raw_parts_mut(ptr.offset(1), len - 1) };
}

fn main() {
    let mut slice = &mut [0, 1, 2, 3][..];
    pop_front(&mut slice);
    println!("{:?}", slice);
}

playground

This program outputs:

[1, 2, 3]

Tags:

Rust