Timo Betcke
Timo Betcke

Reputation: 23

Lifetime elision for &mut self and output values of functions

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

Answers (2)

BallpointBen
BallpointBen

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

Kevin Reid
Kevin Reid

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

Related Questions