I have two functions doing the same thing, but only one compiles without error:
struct Something<'number> {
numbers: &'number [i64]
}
impl<'number> Something<'number> {
fn hasna() {
let dummy = [0, 1, 2, 3];
let s = Self {numbers: &dummy[1..3]};
}
fn bruzzn() {
let dummy = [0, 1, 2, 3];
let s = Something {numbers: &dummy[1..3]};
}
}
fn bruzzn does compile, but hasna doesn't - with the error
77 | impl<'number> Something<'number> {
| ------- lifetime `'number` defined here
78 | fn hasna() {
79 | let dummy = [0, 1, 2, 3];
| ----- binding `dummy` declared here
80 | let s = Self {numbers: &dummy[1..3]};
| -^^^^^------
| ||
| |borrowed value does not live long enough
| this usage requires that `dummy` is borrowed for `'number`
81 | }
| - `dummy` dropped here while still borrowed
Why is using Self a problem when using the struct name works?
First, a local variable necessarily does not outlive a lifetime that is a parameter to the function. In this case: it is always impossible to create an &'number [i64] that points to dummy.
Second, Self is an alias for exactly the type given in the impl. In this case, Self is an alias for Something<'number>. So, if we replace Self with Something<'number>, and notice the lifetime in the other function, we get:
fn hasna() {
let dummy = [0, 1, 2, 3];
let s = Something<'number> {numbers: &dummy[1..3]};
}
fn bruzzn() {
let dummy = [0, 1, 2, 3];
let s = Something::<'_> {numbers: &dummy[1..3]};
}
bruzzn compiles because it creates a Something<'1>, where '1 is the unnamed lifetime of a borrow of the local variable dummy, which contains a reference &'1 [i64].
hasna does not compile because it is trying to create a Something<'number>, which must contain a reference &'number [i64], which cannot be obtained by borrowing the local variable dummy.
bruzzn has a lifetime parameter number — its full name is Something::<'number>::bruzzn — but it does not actually make any use of that lifetime parameter, because it never mentions it in any way, not even via Self.
I think almost everybody’s mental model of lifetimes is not really complete. There’s a lot of subtleties (mostly to do with local variables, not named lifetimes).
one tipe if you ever run into this in non-example code (and need to construct exactly Self due to some other restriction): rust automatically promotes references to expressions to 'static if the expression can be evaluated in const in some cases,
so inlining the variable would also work (if it can be evaluated at compile time)