3rd lifetime rule

The third rule is that, if there are multiple input lifetime parameters, but one of them is &self or &mut self because this is a method, the lifetime of self is assigned to all output lifetime parameters.

Looks like below code is in compliance with 3rd rule, but it doesn't compile, maybe I am misunderstanding rule 3.

I understand that if annote 2nd parameter with lifetime 'a it will be ok.

struct Test<'a> {
    name: &'a str,
}

impl<'a> Test<'a> {
    pub fn longest(&self, str1: & str) -> &str {
        str1
    }
}

The rule is being applied properly, but you're misunderstanding the error:

error: lifetime may not live long enough
 --> src/lib.rs:7:1
  |
6 | pub fn longest(&self, str1: & str) -> &str {
  |                -            - let's call the lifetime of this reference `'1`
  |                |
  |                let's call the lifetime of this reference `'2`
7 | str1
  | ^^^^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`

The second parameter has a different lifetime from the self parameter, and it isn't guaranteed to live longer; therefore the lifetime '1 can't be downcast into the lifetime '2.

The true desugaring of longest looks more like this:

pub fn longest<'x, 'y>(&'x self, str1: &'y str) -> &'x str {
    str1
}

You'll want to add a bound for 'y outliving 'x like this:

pub fn longest<'x, 'y>(&'x self, str1: &'y str) -> &'x str
where
    'y: 'x
{
    str1
}

This may be helpful: Subtyping and Variance - The Rustonomicon

3 Likes

Desugared:

impl<'a> Test<'a> {
    pub fn longest<'s, 'o>(self: &'s Test<'a>, str1: &'o str) -> &'s str {
        str1
    }
}
error: lifetime may not live long enough
 --> src/lib.rs:7:9
  |
6 |     pub fn longest<'s, 'o>(self: &'s Test<'a>, str1: &'o str) -> &'s str {
  |                    --  -- lifetime `'o` defined here
  |                    |
  |                    lifetime `'s` defined here
7 |         str1
  |         ^^^^ method was supposed to return data with lifetime `'s` but it is returning data with lifetime `'o`
  |
  = help: consider adding the following bound: `'o: 's`

The code doesn't work (with elision or when desugared) because there's no relation between 'o and 's; 'o might be shorter than 's and thus the &'o str cannot coerce to an &'s str.

In contrast, when you replace 'o with 'a:

impl<'a> Test<'a> {
    pub fn longest<'s, 'o>(self: &'s Test<'a>, str1: &'a str) -> &'s str {
        str1
    }
}

There is an implicit 'a: 's bound due to the presence of &'s Test<'a> as an input type. The compiler understands that an &'s Test<'a> where 's is shorter than 'a is not well-formed and can never exists, and exploits this knowledge.

You can see that this implicit bound is present in the OP by doing something like this:

impl<'a> Test<'a> {
    pub fn longest(&self, str1: & str) -> &str {
        let s: &'a str = "";
        s
    }
    pub fn longest_more_explicit<'s>(&'s self, str1: & str) -> &'s str {
        let s: &'a str = "";
        let s: &'s str = s;
        s
    }
}
8 Likes

Yes, yes, I have misunderstood the rule, the rule is saying that outputs have same lifetime with the &self, but it never says other parameter is guaranteed to live longer than &self (the output's lifetime).
Thank you bro and thank you @Yokin

1 Like

Here's some documentation on implied bounds.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.