It’s easier to talk about types than values, and generally you can only go so far with an intuition where you’re talking about “the lifetime of {SOME VALUE}
”.
For properly discussing these values, the approach is two-fold; first determine the types of the values you’re talking about, then look at the lifetime parameters in those types.
self
comes from (&'borrow mut self)
argument, which is a short-hand for self: &'borrow mut Self
. The type Self
in an impl<'a, T> Iterator for IterMut<'a, T>
is a short-hand for the self-type IterMut<'a, T>
, hence self: &'borrow mut IterMut<'a, T>
. You see that the type of self
contains two lifetime arguments, 'borrow
and 'a
, there’s not really a single lifetime that anyone could call the lifetime of self
; however often one would still call the first/outermost lifetime 'borrow
“the lifetime of self
”.
Similarly, self.0
is the field of the value behind the reference self
, so it’s the type of the single/first field of IterMut<'a, T>
, which is &'a mut [T]
. Here’s only a single lifetime, so in some ways it makes sense to say “the lifetime of self.0
is 'a
”.
So I’d mostly agree with your statement cited above, even though one could be more precise
It doesn’t extend any lifetime. Maybe a good analogy is something like: If you have a reference foo: &'a mut i32
and then do let bar: i32 = *foo
, the value bar
can exist longer than the lifetime 'a
, but all that happened here is that the value behind the reference foo
was copied. Since i32
is not a type involving any lifetimes at all, you wouldn’t consider any lifetime to be extended here.
A reference &'a mut T
does not mean that the value behind the reference is only alive for 'a
. Instead it means that the reference itself is only alive for that long, which could be due to the memory behind that reference being short-lived; e.g. if some API passes you &'a mut T
, it might as well deallocate the memory behind that &'a mut T
after the lifetime 'a
ends (as well as dropping whatever happens to be in that memory at the time). What’s explicitly not prohibited is that the value that’s in that memory gets replaced by any other value, so that the original value of type T
could be kept for much longer than 'a
.
Double mutable references, i.e. values of type &'short mut &'long mut S
are potentially confusing in this regard, because the most natural way to access them – i.e. dereferencing – only gives you a &'short mut S
. But if you use mem::replace
, you can obtain a &'long mut S
: That’s a case of dereferencing shortening the lifetime of the contained &'long mut S
, not a case of mem::replace
extending a lifetime. In fact, mem::replace
doesn’t care at all whether the type behind a reference contains a lifetime at all. It works in general with dest: &'short mut T
and src: T
and returns a T
. The fact that T == &'long mut S
introduces a second lifetime to the story adds complexity, but nothing really changes. Replace equal types with equal types and you have that dest: &'short mut &'long mut S
and src: &'long mut S
as arguments to mem::replace
makes it return a &'long mut S
.
The type &'borrow mut IterMut<'a, T>
in the example, too, is (almost) an instance of such a double mutable reference type: IterMut<'a, T>
is a newtype-wrapper struct around &'a mut [T]
, so that &'borrow mut IterMut<'a, T>
is in many ways the same as &'borrow mut &'a mut [T]
. It, too, has that property that interacting “naively” with self.0
(i.e. not using something like mem::replace
) can often result in either compilation failure (if you try to move that value) or dereferencing/re-borrowing might “help” you out but only give you access to a &'borrow mut [T]
.