Reputation: 23
I am trying to better understand lifetime elisions for &mut self
and corresponding output parameters.
Let's say I have a function signature of the form
fn foo(&mut self) -> &mut bar
From the lifetime section in the Rustonomicon I understand that this gets expanded to
fn foo<'a>(&'a mut self) -> &'a mut bar
which has the side effect that self
gets mutably borrowed as long as the corresponding object exists.
What I would like to know is what happens if I specify a function with signature
fn foo<'a>(&mut self) -> &'a mut bar
In my understanding, &mut self
is elided and according to elision rules gets its own input lifetime. So we would end up with something like
fn foo<'b, 'a>(&'b mut self) -> &'a mut bar
According to elision rules, 'b
gets assigned to all elided output lifetimes. But 'a
is not elided. So what is the lifetime 'a
? Code with such a signature compiles fine. But I don't quite understand the lifetime implications for 'a
.
Upvotes: 2
Views: 225
Reputation: 13750
Like any other generic parameter, 'a
can be freely chosen by the caller as long as it satisfies any generic bounds on the trait (which there aren't any of, in your example). Just as you have something like
// caller chooses the value of B in `fn collect<B>(self) -> B` to be Vec<_>
let items: Vec<_> = my_data.into_iter().collect();
you can have
// caller chooses the value of 'a in `fn foo<'a>(&mut self) -> &'a mut bar` to be 's
's: {
let b = obj.foo()
}
Of course, Rust won't even compile foo
if it can't prove that 'a
can indeed be freely chosen by the caller, which, in the absence of other lifetime bounds, means we must have 'a: 'static
, and the body of foo
must be compatible with that.
Upvotes: 0
Reputation: 43743
You've correctly un-elided the lifetimes.
So what is the lifetime
'a
?
Whenever a function has a lifetime parameter, it is always: "whatever the caller wants it to be" (subject to bounds, which there aren't in this case).
Code with such a signature compiles fine.
Did you try writing a body for that function? Given those lifetimes, it can't actually do very much. For example,
impl Foo {
fn foo<'b, 'a>(&'b mut self) -> &'a mut Bar {
&mut self.bar
}
}
will fail to compile with errors telling you (indirectly) that this is only valid if 'b
outlives 'a
, which it doesn't.
If you add that "outlives" relation to the signature, then the code can compile:
impl Foo {
fn foo<'b, 'a>(&'b mut self) -> &'a mut Bar
where
'b: 'a
{
&mut self.bar
}
}
However, this is almost never any more useful than the simpler
fn foo<'a>(&'a mut self) -> &'a mut Bar {
because references can generally be used as if they have shorter lifetimes than their type specifies, so 'a
here can serve the purpose of 'a
and the purpose of 'b
in the more complex declaration.
Upvotes: 2