Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 398
lncr Avatar asked Aug 23 '17 07:08

lncr


People also ask

What is the meaning of the phrase difference between?

1. countable noun. The difference between two things is the way in which they are unlike each other. That is the fundamental difference between the two societies. [ + between]

What is difference between among and between?

The most common use for among is when something is in or with a group of a few, several, or many things. The most common use of between is when something is in the middle of two things or two groups of things. It is sometimes used in the phrase in between.

What is the difference between this these?

This and these are used to point to something near you. For a singular thing, use this. For a plural thing, use these.


1 Answers

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

like image 130
red75prime Avatar answered Oct 12 '22 23:10

red75prime