Subslice lifetimes


#1

If I have a slice, then I can get a subslice of it:

fn main() {
    let backing_array = [0, 1, 2, 3, 4];
    println!("Backing array: {:?}", backing_array);
    let slice = &backing_array[1..];
    let subslice = &slice[1..];
    println!("Subslice: {:?}", subslice);
}

The subslice can even outlive its “parent”, as long as the backing store outlives them both:

fn main() {
    let backing_array = [0, 1, 2, 3, 4];
    println!("Backing array: {:?}", backing_array);
    let subslice = {
        let slice = &backing_array[1..];
        &slice[1..]
    };
    println!("Subslice: {:?}", subslice);
}

We can even do the same for a 2D array:

fn main() {
    let backing_array = [[0, 1, 2, 3, 4]; 2];
    println!("Backing array: {:?}", backing_array);
    let subslices = {
        let slices = backing_array
        .iter()
        .map(|arr| &arr[1..])
        .collect::<Vec<&[u8]>>();
        
        slices
        .iter()
        .map(|slice| &slice[1..])
        .collect::<Vec<&[u8]>>()
    };
    println!("Subslices: {:?}", subslices);
}

And then make a custom Matrix wrapper, as long as we’re careful with the lifetimes:

#[derive(Debug)]
struct Matrix<'a> {
    lines: Vec<&'a [u8]>,
}

impl<'a> Matrix<'a> {
    fn skip_cols<'b>(&self, cols: usize) -> Matrix<'b> where 'a: 'b {
        Matrix {
            lines: self.lines.iter().map(|line| &line[cols..]).collect()
        }
    }
}

fn main() {
    let example_line = [0, 1, 2, 3, 4];
    let backing_vec = [&example_line[..]; 2].to_vec();
    let matrix = Matrix { 
        lines: backing_vec,
    };
    println!("Matrix: {:?}", matrix);
    let subsubmatrix = {
        let submatrix = matrix.skip_cols(1);
        submatrix.skip_cols(1)
    };
    println!("Subsubmatrix: {:?}", subsubmatrix);
}

We can also make the slices mutable:

fn main() {
    let mut backing_array = [0, 1, 2, 3, 4];
    println!("Backing array: {:?}", backing_array);
    let subslice = {
        let slice = &mut backing_array[1..];
        &mut slice[1..]
    };
    println!("Subslice: {:?}", subslice);
    subslice[0] += 1;
    println!("Subslice: {:?}", subslice);
}

However, making the 2D subslices mutable seems to break down completely (because subslices’ lifetime must now be a subset of slices’):

fn main() {
    let mut backing_array = [[0, 1, 2, 3, 4]; 2];
    println!("Backing array: {:?}", backing_array);
    let subslices = {
        let mut slices = backing_array
        .iter_mut()
        .map(|arr| &mut arr[1..])
        .collect::<Vec<&mut [u8]>>();
        
        slices
        .iter_mut()
        .map(|slice| &mut slice[1..])
        .collect::<Vec<&mut [u8]>>()
    };
    println!("Subslices: {:?}", subslices);
}
error[E0597]: `slices` does not live long enough
  --> src/main.rs:10:9
   |
10 |         slices
   |         ^^^^^^ borrowed value does not live long enough
...
14 |     };
   |     - `slices` dropped here while still borrowed
15 |     println!("Subslices: {:?}", subslices);
16 | }
   | - borrowed value needs to live until here

Am I misunderstanding something? Is this a borrowck bug?


#2

The issue doesn’t seem to have anything to do with slices, from what I can tell the &muts in a Vec can’t outlive the Vec, while &s can.

This works fine:

fn main() {
    let backing_array = [[0, 1, 2, 3, 4]; 2];
    println!("Backing array: {:?}", backing_array);
    let subslices = {
        let mut slices = Vec::<&[u8]>::new();
        slices.push(&backing_array[0]);
        
        let mut subslices = Vec::<&[u8]>::new();
        subslices.push(&slices[0]);
        subslices
    };
    println!("Subslices: {:?}", subslices);
}

While this doesn’t:

fn main() {
    let mut backing_array = [[0, 1, 2, 3, 4]; 2];
    println!("Backing array: {:?}", backing_array);
    let subslices = {
        let mut slices = Vec::<&mut [u8]>::new();
        slices.push(&mut backing_array[0]);
        
        let mut subslices = Vec::<&mut [u8]>::new();
        subslices.push(&mut slices[0]);
        subslices
    };
    println!("Subslices: {:?}", subslices);
}

#3
subslices.push(&mut slices[0])

What happens here is that this new &mut reference is a reborrow of slices, thus having a shorter lifetime. If you want the values in subslices “untangled” from the slices variable, you need to move the reference from slices. For example:

subslices.push(slices.remove(0))

To fix your example from your previous post, you just need to change the second iter_mut to into_iter.

Shared references don’t have the same problem as mutable references as they’re always can be copied, but mutable references can be only moved or reborrowed.

If you want to learn more about reborrow vs move of mut-references, just search “reborrow” and I’m sure a few in-depth topics will appear.


#4

Oh, guess I should have realized that when Vec.clone didn’t work.