Stream: t-compiler/wg-nll

Topic: what-part-of-nll-allows-this


Jake Goulding (Jul 12 2018 at 02:45, on Zulip):

From a SO question:

struct Superhero<'a> {
    name: &'a String,
    power: &'a i32,
}

fn main() {
    let n = "Bruce Wayne".to_string();
    let r;
    {
        let p = 98;
        {
            let hero = Superhero {
                name: &n,
                power: &p,
            };
            r = hero.name;
        }
        println!("{}", r);
    }
}

This fails before NLL and compiles with NLL, but I don't understand which part of NLL enables it. Pre NLL, my argument would have been:

Since Superhero has a single lifetime for both fields, when you create it, 'a is unified to be the intersection of the variables, which is the inner scope. When you try to get hero.name back out, the fact that it came from a longer lifetime originally is lost, so you get the error.

You can fix this by assigning two distinct lifetimes for each field

Jake Goulding (Jul 12 2018 at 02:47, on Zulip):

But AIUI, that same argument would be true in NLL world. Is hero in some weird superposition of lifetimes? Is the MIR borrowchecker clever enough to see that power is never used so it never plays a part in lifetimes?

nikomatsakis (Jul 12 2018 at 05:11, on Zulip):

You are correct that r cannot outlive p or n. The thing that makes this work under NLL is that we consider r to be "live" only until its last use, and not until the end of the block it is declared in. In this case, that last use is within the scope of p, so everything works. But this variant, where I moved the println! outside of the block declaring p, fails to compile:

#![feature(nll)]

struct Superhero<'a> {
    name: &'a String,
    power: &'a i32,
}

fn main() {
    let n = "Bruce Wayne".to_string();
    let r;
    {
        let p = 98;
        {
            let hero = Superhero {
                name: &n,
                power: &p,
            };
            r = hero.name;
        }
    }
    println!("{}", r); // <-- I moved this to outside of the scope of `p`
}
nikomatsakis (Jul 12 2018 at 05:11, on Zulip):

I get:

error[E0597]: `p` does not live long enough
  --> src/main.rs:16:24
   |
16 |                 power: &p,
   |                        ^^ borrowed value does not live long enough
...
20 |     }
   |     - borrowed value only lives until here
21 |     println!("{}", r);
   |                    - borrow later used here
nikomatsakis (Jul 12 2018 at 05:12, on Zulip):

It would be nice -- and isn't out of the question at some point -- if we could highlight the connection to Superhero's single lifetime parameter

Last update: Nov 21 2019 at 23:25UTC