DerefMut and lifetimes

Hi

I've been trying to make my implementation for Iterator over mutable slice.
I know how to fix the code below, but I don't quite understand why the following code does not work (I've added explicit lifetimes for clarity here).
I will write how I understand it,and would be great if someone corrected me if I'm wrong

struct MyIterMut<'a> {
    slice: &'a mut [u8],
}

impl<'a> Iterator for MyIterMut<'a> {
    type Item = &'a u8;

    fn next<'b>(&'b mut self) -> Option<Self::Item>{
        if (self.slice.is_empty()) { return None;}

        let (l, r) = self.slice.split_at_mut(1);
        self.slice = r;
        l.get_mut(0)
    }
}

The compiler says

error: lifetime may not live long enough
  --> src\main.rs:13:9
   |
5  | impl<'a> Iterator for MyIterMut<'a> {
   |      -- lifetime `'a` defined here
...
8  |     fn next<'b>(&'b mut self) -> Option<Self::Item>{
   |             -- lifetime `'b` defined here
...
13 |         l.get_mut(0)
   |         ^^^^^^^^^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `
|
   = help: consider adding the following bound: `'b: 'a`

Since self is only borrowed, I cannot move self.slice with lifetime 'a into a local variable and proceed with it. I can only reborrow it to call split_at_mut() on slice (this method borrows mutably).
So self.slice gets lifetime 'b (because self has lifetime 'b). And its complete type is &'b mut &'a mut [u8]

And then comes the piece that I don't quite understand. The type &'b mut &'a mut [u8] does not have method split_at_mut(), but &mut [u8] does have it.
So &'b mut &'a mut [u8] should be derefenced via DerefMut and it gets lifetime 'b being derferenced to &'b mut [u8] because lifetime elision happens in defef_mut(), which signature is

fn deref(&self) -> &Self::Target {}

So split_at_mut() gets called on a reference with lifetime 'b, so l and r get lifetime 'b as well through lifetime elision and all breaks because the function is supposed to return a reference with lifetime 'a.

Is this understanding correct?

1 Like

Yes, you have completely understood why the code doesn't compile. Good work! The same problem also exists in the call l.get_mut(0).

The solution to the first problem is to make sure the reference is moved into the operation you use on it, which can be done with std::mem::take(). For the second, you can destructure the slice reference…

let ([l], r) = std::mem::take(&mut self.slice).split_at_mut(1);
self.slice = r;
Some(l)

...or you can use split_first_mut() which also helps check if the slice is empty.

fn next<'b>(&'b mut self) -> Option<Self::Item> {
    let (l, r) = std::mem::take(&mut self.slice).split_first_mut()?;
    self.slice = r;
    Some(l)
}
1 Like

Thank you!

That helped me a lot!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.