简体   繁体   中英

What is the difference between '&self' and '&'a self'?

I recently had an error which was simply resolved by changing

impl<'a> Foo<'a> {
    fn foo(&'a self, path: &str) -> Boo<'a> { /* */ }
}

to

impl<'a> Foo<'a> {
    fn foo(&self, path: &str) -> Boo { /* */ }
}

which did not make sense according to my understanding, as I thought that the second version is exactly the same as the first with applied lifetime elision.


In case we introduce a new lifetime for the method this seems to be the case according this example from the nomicon .

fn get_mut(&mut self) -> &mut T;                        // elided
fn get_mut<'a>(&'a mut self) -> &'a mut T;              // expanded

So what are the differences between this and my first code snipped.

Lifetime 'a in fn foo(&'a self, ...) ... is defined for impl<'a> , that is it is the same for all foo calls.

Lifetime 'a in fn get_mut<'a>(&'a mut self) ... is defined for the function. Different calls of get_mut can have different values for 'a .

Your code

impl<'a> Foo<'a> { fn foo(&'a self, path: &str) -> Boo<'a> { /* */ } }

is not the expansion of elided lifetime. This code ties lifetime of borrow &'a self to the lifetime of structure Foo<'a> . If Foo<'a> is invariant over 'a , then self should remain borrowed as long as 'a .

Correct expansion of elided lifetime is

impl<'a> Foo<'a> {
    fn foo<'b>(&'b self, path: &str) -> Boo<'b> { /* */ }
}

This code doesn't depend on variance of structure Foo to be able to borrow self for shorter lifetimes.

Example of differences between variant and invariant structures.

use std::cell::Cell;

struct Variant<'a>(&'a u32);

struct Invariant<'a>(Cell<&'a u32>);

impl<'a> Variant<'a> {
    fn foo(&'a self) -> &'a u32 {
        self.0
    }
}

impl<'a> Invariant<'a> {
    fn foo(&'a self) -> &'a u32 {
        self.0.get()
    }
}

fn main() {
    let val = 0;
    let mut variant = Variant(&val);// variant: Variant<'long>
    let mut invariant = Invariant(Cell::new(&val));// invariant: Invariant<'long>
    {
        let r = variant.foo();
        // Pseudocode to explain what happens here
        // let r: &'short u32 = Variant::<'short>::foo(&'short variant);
        // Borrow of `variant` ends here, as it was borrowed for `'short` lifetime

        // Compiler can do this conversion, because `Variant<'long>` is
        // subtype of Variant<'short> and `&T` is variant over `T`
        // thus `variant` of type `Variant<'long>` can be passed into the function 
        // Variant::<'short>::foo(&'short Variant<'short>)
    }
    // variant is not borrowed here
    variant = Variant(&val);

    {
        let r = invariant.foo();
        // compiler can't shorten lifetime of `Invariant`
        // thus `invariant` is borrowed for `'long` lifetime
    }
    // Error. invariant is still borrowed here
    //invariant = Invariant(Cell::new(&val));
}

Playground link

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM