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