I don't understand why I am getting the following compiler error from this code:
struct Superhero<'a> { name: &'a String, power: &'a i32 } // 1
// 2
fn main() { // 3
let n = "Bruce Wayne".to_string(); // 4
let r; // 5
{ // 6
let p = 98; // 7
{ // 8
let hero = Superhero{ name: &n, power: &p }; // 9
r = hero.name; // 10
} // 11
println!("{}", r); // 12
} // 13
} // 14
Compiler Error: rustc 1.27.1 (5f2b325f6 2018-07-07)
error[E0597]: `p` does not live long enough
--> src/main.rs:9:53
|
9 | let hero = Superhero{ name: &n, power: &p };
| ^ borrowed value does not live long enough
...
13 | }
| - `p` dropped here while still borrowed
14 | }
| - borrowed value needs to live until here
Here is what I thought this code would do, line by line. There is something wrong with one or more of these lines, because this code does not compile.
4: Initialize name: String to "Bruce Wayne".to_string();
5: Declare r to be initialized in a different scope
6: Begin a new scope (A)
7: Initialize p to 98
8: Begin a new scope (B)
9: Initialize hero: Superhero to a new struct
hero.name refers to the variable{n},
hero.power refers to the variable{p}
10: Copy a reference to the variable{n},
since reference types are copy, both hero.name and r are distinct references to the variable{n}.
11: End scope (B): hero and the two references it owns {hero.name, hero.power} are dropped.
12: Print the value of the variable{r}: Should print "Bruce Wayne"
13: End scope (A): the variable{p} is dropped.
14: End scope for main. The variables {n, r} are dropped.
Why does the compiler error say that something is still borrowing p
on line 13? Shouldn't hero
(and subsequently hero.power
) have been dropped on line 11? There should be nothing referring to p
at this point.
Curiously, changing the order in which the values (p
and r
) are initialized fixes the issue, and I have no idea why.
Ways to fix:
let p = 90;
between line 4 and line 5let r;
between line 7 and line 8In both of these cases, simply declaring r
AFTER p
ensures that nothing is still "borrowing" p
when it is dropped. This makes no sense to me at all, because I feel like r
has nothing at all to do with p
, or with anything that may be borrowing p
.
This code runs with non-lexical lifetimes enabled.
What property of lexical lifetimes causes this to not compile, and what about non-lexical lifetimes fixes this issue?
This is only a guess, but here is what I think happens:
Superhero
, you stated that name
and power
should have the same lifetime.r
is inferred as &String
(or maybe &str
, the point is that r
is a reference). With lexical lifetimes, r
must live until the end of the block in which it is declared, so until line 14.hero.name
to r
, hero.name
should live at least as long as r
, therefore hero.name
should live until line 14.hero.name
and hero.power
should have the same lifetime per the struct declaration, hero.power
should also live until line 14.hero.power
borrows p
, p
should live until line 14, but it only lives until the end of the block in which it is declared (line 13).The reason it works with non-lexical lifetimes is because the compiler notices that you don't use r
after line 12 and is therefore able to shorten the lifetimes accordingly. Note that it doesn't work even with nll if you use r
after the closing brace of line 13.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With