3

How do I resolve this error? What exactly am I telling the compiler when I use the "anonymous lifetime" in impl?

struct LineHandlerInfo<'a> {
    label: &'a str,
    match_literal: &'a str,
    f: fn(&str) -> Option<&str>,
}

struct Game<'a> {
    handlers: Vec<LineHandlerInfo<'a>>,
}

impl Game<'_> {
    fn match_str<'a>(
        &'a mut self,
        label: &'a str,
        match_literal: &'a str,
        mut f: fn(&str) -> Option<&str>,
    ) {
        let mut lh = LineHandlerInfo {
            label,
            match_literal,
            f,
        };
        self.handlers.push(lh);
    }
}

fn main() {
    let mut g = Game {
        handlers: Vec::new(),
    };
    g.match_str("echo hello", "hello", |s| {
        println!("{}", s);
        None
    });
}

When I attempt to compile, I get the following error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/main.rs:18:22
   |
18 |         let mut lh = LineHandlerInfo {
   |                      ^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime 'a as defined on the method body at 12:18...
  --> src/main.rs:12:18
   |
12 |     fn match_str<'a>(
   |                  ^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:19:13
   |
19 |             label,
   |             ^^^^^
note: but, the lifetime must be valid for the lifetime '_ as defined on the impl at 11:11...
  --> src/main.rs:11:11
   |
11 | impl Game<'_> {
   |           ^^
   = note: ...so that the expression is assignable:
           expected LineHandlerInfo<'_>
              found LineHandlerInfo<'_>

How do I resolve this error and what exactly am I telling the compiler when I specify a lifetime on impl Game when I already have a lifetime on the struct?

Ultrasaurus
  • 3,031
  • 2
  • 33
  • 52
  • based reading links from @shepmaster and with a lot of trial and error, I've fixed the compiler error; https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=208beb18e2530b97f5ccf15102790a63 -- would be nice to have some more reference to what exactly this syntax is mean to convey – Ultrasaurus Oct 17 '19 at 03:07
  • I removed the other variation I tried which @shepmaster says is a different issue -- leaving here for reference: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=00449a690ba597eccc405ef1f8a951a8) – Ultrasaurus Oct 18 '19 at 13:40

2 Answers2

13

How do I resolve this error and what exactly am I telling the compiler when I specify a lifetime on impl Game when I already have a lifetime on the struct?

I suspect your confusion stems from an incomplete understanding of the way in which lifetimes are declared and used in Rust.

Struct Lifetimes

In order to use a lifetime on a struct, you declare the lifetime inside the <> adjacent to the name of the struct you are declaring, and then refer to that lifetime inside the struct definition. Importantly, note that the lifetime declared there is scoped to the struct definition - it has no meaning outside.

For example (using the MRE that @Shepmaster provided):

struct Game<'a> {
    handlers: Vec<&'a str>,
}

The struct Game contains a vector of references to strings, and the strings which are referenced must last at least as long as the Game struct.

Impl Lifetimes

When using a lifetime specifier on an impl block, you declare the lifetime inside the <> adjacent to the impl keyword, after which you may refer to the lifetime both in the struct being implemented, and inside the implementation itself, like this:

impl<'b> Game<'b> {
    fn match_str(&mut self, label: &'b str) {
        self.handlers.push(label);
    }
}

Note that I am using an entirely different lifetime name here ('b) to illustrate that the the lifetime declaration on the struct is independent of the one on the impl block.

Breaking this down:

impl<'b>

This means that we are defining an implementation for a struct, and within that definition we will use the lifetime 'b

 Game<'b> {

This means that the impl is for the struct Game with lifetime 'b - so any references to self inside this implementation are going to automatically have lifetime 'b as well.

fn match_str(&mut self, label: &'b str) {

Here we define the method match_str which takes an argument label. label is a string slice which also has the lifetime 'b - so it must last at least as long as the self that the method is called on.

In your original code, you had something like this:

impl Game<'_> {
    fn match_str<'a>(&mut self, label: &'a str) {
    ...
    }
}

This was telling the compiler:

  • That you are starting a new impl block that, and there are no lifetimes declared at impl level
  • That the implementation is for the struct Game; this struct has a lifetime parameter but we don't care about it and we are not going to tie it to any element of the implementation
  • We are defining a method match_str, and we are declaring a lifetime 'a which we can refer to in the rest of the function signature
  • We have an argument label which has the lifetime a, but we aren't relating this lifetime to anything else

More information:

harmic
  • 28,606
  • 5
  • 67
  • 91
  • 1
    Wonderful explanation. The breakdown provided the info I needed. Also great to have the doc reference I had missed the detail that impl lifetime indicates that `self` inside this implementation will automatically have impl lifetime. – Ultrasaurus Oct 31 '19 at 10:19
  • Game changing explanation @harmic. One small query regarding the last point "but we aren't relating this lifetime to anything else". If `match_str` returns a reference(and we specify lifetime `a), then that would mean that we are relating the lifetime of return value to `label: &'a str`? – Haris Muzaffar Apr 07 '22 at 02:18
  • @HarisMuzaffar yes, if we had this `fn match_str<'a>(&mut self, label: &'a str) -> &'a str` then the returned value has a lifetime no longer than the `label` parameter. Also note that if you return a reference from a method and do not annotate it's lifetime, then it will automatically have the same lifetime as `self` (this is called [lifetime elision](https://doc.rust-lang.org/nomicon/lifetime-elision.html)) – harmic Apr 07 '22 at 03:43
1

How do I resolve this error?

Remove the generic lifetime from the function, provide a name for the lifetime on the impl block instead of using the anonymous lifetime, then use the named lifetime in the function arguments. Remove the lifetime from &self:

impl<'a> Game<'a> {
    fn match_str(&mut self, label: &'a str, match_literal: &'a str, f: fn(&str) -> Option<&str>) {
        self.handlers.push(LineHandlerInfo {
            label,
            match_literal,
            f,
        });
    }
}

See also:

What exactly am I doing when I use the "anonymous lifetime" in impl?

You are effectively stating "I know there's a lifetime here, but I don't care about it". However, that's not true for your case; you do care about the lifetime that parameterizes the type because that's what your variables need to match.

See also:

for a struct with a function pointer in it

This has nothing to do with function pointers. When encountering problems while programing, I recommend creating a minimal, reproducible example, stripping out things that don't make the error go away. This allows you to focus on exactly the problem at hand. For example, this reproduces the same error:

struct Game<'a> {
    handlers: Vec<&'a str>,
}

impl Game<'_> {
    fn match_str<'a>(&mut self, label: &'a str) {
        self.handlers.push(label);
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • @Ultrasaurus there are more [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. – Shepmaster Oct 17 '19 at 01:17
  • I still don't understand what is is exactly about the example that means that an anonymous lifetime won't work. For other examples where the struct has a named lifetime don't need a named lifetime for impl. – Ultrasaurus Oct 17 '19 at 01:49
  • The isolated example that is provided in this answer shows a different error. I attempted to apply the fix and still see the same error: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c7b05b72fcaf32e3dde9b761661ce556 – Ultrasaurus Oct 17 '19 at 02:26
  • You did not apply the same fix. The function should not have any generic lifetimes. – Shepmaster Oct 17 '19 at 02:28
  • I've updated the question with a smaller example that also adds more explicitly named lifetimes in all of the places where I know to add them. The information in this answer was helpful, but I'm still not seeing where there remains a generic lifetime and how to resolve the error. – Ultrasaurus Oct 17 '19 at 02:40
  • @Ultrasaurus then it’s a duplicate of the question I’ve linked in this answer and another one I’ll have to find tomorrow and the effort of this answer has been invalidated. You are trying to store something with lifetime X in a struct that holds lifetime Y. There’s no relation between those two, so the compiler fails. Additionally, you are saying that the lifetime of the thing you are storing has to be exactly the same as the thing you store it in, which is almost never possible. – Shepmaster Oct 17 '19 at 02:45
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/200991/discussion-between-ultrasaurus-and-shepmaster). – Ultrasaurus Oct 17 '19 at 02:56