Need help in resolving lifetime issue for borrowed slice

I have a writer which takes slice and returns a slice that it wrote

struct Writer<'data> {
    buffer: &'data mut [u8],
}

impl<'data> Writer<'data> {
    fn write(&mut self) -> &[u8] {
        &self.buffer[..]
    }
}

fn foo(buf: &mut [u8]) -> &[u8] {
    let mut writer = Writer { buffer: buf };
    writer.write()
}

for this i get an error saying

error[E0515]: cannot return reference to local variable `writer`
  --> src/main.rs:13:5
   |
13 |     writer.write()
   |     ^^^^^^^^^^^^^^ returns a reference to data owned by the current function

since data has separate lifetime than buf, i tried to separate it as

struct Writer<'data> {
    buffer: &'data mut [u8],
}
impl<'data, 'me> Writer<'data>
where
    'data: 'me,
{
    fn write(&'me mut self) -> &'data [u8] {
        &self.buffer[..]
    }
}

fn foo(buf: &mut [u8]) -> &[u8] {
    let mut writer = Writer { buffer: buf };
    writer.write()
}

but now getting

   |
6  | impl<'data, 'me> Writer<'data>
   |      -----  --- lifetime `'me` defined here
   |      |
   |      lifetime `'data` defined here
...
11 |         &self.buffer[..]
   |         ^^^^^^^^^^^^^^^^ method was supposed to return data with lifetime `'data` but it is returning data with lifetime `'me`
   |
   = help: consider adding the following bound: `'me: 'data`

i tried inverting the bound as 'me: 'data but i get

error[E0515]: cannot return reference to local variable `writer`
  --> src/main.rs:17:5
   |
17 |     writer.write()
   |     ^^^^^^^^^^^^^^ returns a reference to data owned by the current function

References have this special ability where they can go out of scope while reborrows that happen through them are not invalidated. But unfortunately, there's no way to emulate this with a nominal struct and methods like you have here. The API contract of the method requires that Writer remain borrowed for the duration of the returned references, as opposed to just *writter.buffer remaining borrowed.

So once the Writer goes out of scope at the end of foo, any references obtained through Writer::write are invalidated. You are in effect trying to return a borrow of a local which is going out of scope.

One alternative is to return something like a range so that you can construct the written slice from the original data slice (after discarding the Writer).

2 Likes

one possibility would be to make fn write to take self by value and offer a re_borrow(&mut self) -> Writer<'_> method. The call sites must decide whether they need a "re-borrow, then continue using the original later" kind of access (in which case .re_borrow().write() would be used) or a "re-borrow but actually it's like a move" kind of access. With true mutable references most use-cases should be covered between these two. The only remaining downside is the need to explicitly say whether or not re_borrow is needed, at the call site).

In fact, the standard library with Pin<&mut T> does a similar thing. Methods using it would typically take an owned Pin<&mut T> argument, and users that need a re-borrow will need to call Pin::as_mut (but users that need the full lifetime to be available can not call as_mut as it will relate the lifetime of the re-borrow to the variable holding the original Pin<&mut T>).

3 Likes

Good point :smiley:

1 Like