1

I'm trying to understand lifetimes and storing mutable slices inside a struct.

I came up with this example with a struct with a slice and a take function that will return n elements (if present) and store the rest in the structure itself. This code does not compile.

fn main() {
    let mut v: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let mut blah = Blah { slice: &mut v[..] };

    let b = blah.take(5);

    println!("b: {:?}", b);
}

#[derive(Debug)]
struct Blah<'a> {
    slice: &'a mut [u8],
}

impl<'a> Blah<'a> {
    pub fn take(&'a mut self, n: usize) -> Option<Self> {
        if self.slice.len() > n {
            let blah = Blah {
                slice: &mut self.slice[..n],
            };
            self.slice = &mut self.slice[n..];
            Some(blah)
        } else {
            None
        }
    }
}

Compiler error:

error[E0499]: cannot borrow `*self.slice` as mutable more than once at a time
  --> src/main.rs:21:31
   |
15 | impl<'a> Blah<'a> {
   |      -- lifetime `'a` defined here
...
19 |                 slice: &mut self.slice[..n],
   |                             ---------- first mutable borrow occurs here
20 |             };
21 |             self.slice = &mut self.slice[n..];
   |                               ^^^^^^^^^^ second mutable borrow occurs here
22 |             Some(blah)
   |             ---------- returning this value requires that `*self.slice` is borrowed for `'a`

I have a large in-memory buffer that I don't want to copy. Instead, I want to keep referring to the same memory by carrying around "fat pointers" (something like offset + length).

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
user3169543
  • 1,499
  • 1
  • 10
  • 16

1 Answers1

4

The Rust compiler isn't able to detect that the two borrows of sub-slices are non-overlapping. When you borrow &mut self.slice[..n], the whole of self.slice is considered to be borrowed, so you can't then borrow the remaining elements.

There is a method split_at_mut, designed to solve this problem by producing two disjoint mutable borrows from a slice. Your code can be updated to use it like this:

impl<'a> Blah<'a> {
    pub fn take(&'a mut self, n: usize) -> Option<Self> {
        if self.slice.len() > n {
            let (left, right) = self.slice.split_at_mut(n);
            let blah = Blah {
                slice: left
            };
            self.slice = right;
            Some(blah)
        } else {
            None
        }
    }
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204