1
votes

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?

2
based reading links from @shepmaster and with a lot of trial and error, I've fixed the compiler error; play.rust-lang.org/… -- would be nice to have some more reference to what exactly this syntax is mean to conveyUltrasaurus
I removed the other variation I tried which @shepmaster says is a different issue -- leaving here for reference: play.rust-lang.org/…)Ultrasaurus

2 Answers

6
votes

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:

1
votes

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);
    }
}