Lifetime in struct

Hi, I wrote a simple struct (exercise on exercism.io) which have a reference element. And I added some lifetime based on compiler advice, but it still failed. Any suggestion? Thanks!

pub struct HighScores<'a> {
    scores: &'a [u32]
}

impl HighScores<'_> {
    // scores is slice
    pub fn new(scores: &[u32]) -> Self {
        HighScores {scores }
    }
}
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src\lib.rs:11:9
   |
11 |         HighScores {scores }
   |         ^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 10:5...
  --> src\lib.rs:10:5
   |
10 |     pub fn new(scores: &[u32]) -> Self {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src\lib.rs:11:21
   |
11 |         HighScores {scores }
   |                     ^^^^^^
note: but, the lifetime must be valid for the lifetime `'_` as defined on the impl at 8:17...
  --> src\lib.rs:8:17
   |
8  | impl HighScores<'_> {
   |                 ^^
note: ...so that the expression is assignable
  --> src\lib.rs:11:9
   |
11 |         HighScores {scores }
   |         ^^^^^^^^^^^^^^^^^^^^
   = note: expected `HighScores<'_>`
              found `HighScores<'_>`

Let's assign some numbers to those elided and anonymous lifetimes (not real Rust syntax):

impl HighScores<'1> {
    //          ^^
    pub fn new(scores: &'2 [u32]) -> Self { ... }
    //                  ^^
}

Self is HighScores<'1>, so what this says is that new can take a slice of any lifetime '2 and return a HighScores of any lifetime '1. What you want to say is that you can take a slice of some lifetime and return a HighScores of the same lifetime:

impl<'a> HighScores<'a> {
    pub fn new(scores: &'a [u32]) -> Self { ... }
}

Since new returns Self, its argument has to be a reference of the same lifetime as Self. To do that we have to declare a named lifetime parameter and use it both in the impl and in the signature of new.

Lifetime elision doesn't mean lifetime inference: the compiler isn't looking at how you're using the lifetimes and filling them in with what makes sense. It's a pretty simple set of rules that only takes into account the current signature. An elided lifetime ('2) never unifies with a lifetime parameter from an outer scope ('1).

4 Likes

Thanks!

impl<'a>

is define a lifetime 'a on impl level

HighScores<'a> {

means all self in impl block will automatically have lifetime 'a

Related:

Does that mean <_> is not so much “fill-in the blank” but rather a single lifetime using the underscore as a symbol (say, instead of using something like a)?

1 Like
impl HighScores<'_> {

is equal to

impl <'x> HighScores<'x> {

We only know that they (two 'x that given by compiler) are the same, but we can't use this 'x in other area.

https://doc.rust-lang.org/nightly/edition-guide/rust-2018/ownership-and-lifetimes/the-anonymous-lifetime.html

(Not sure I completely understand, but) The only thing using 'a on the impl does is give a name to "the lifetime parameter of HighScores in Self". If you never write 'a inside the impl block, it's exactly the same as writing impl HighScores<'_>.

Elided (or anonymous) lifetimes are never "filled in" with lifetimes from outer scopes whether they're named or not.

It's not really either of those things: '_ just means "I have to write a lifetime because the syntax demands it, but I still want whatever lifetime elision would give". The lifetime elision rules are supposed to make the most common lifetime patterns more concise. If what you want is not what lifetime elision would give you, you can't use '_.

1 Like