What is the exactly NLL rules?

code from https://github.com/rust-lang/rust/issues/70841.
My question is since NLL is born to be None lexical lifetime.
Then why NLL can't make p's lifetime to begin at p = pending(Inspector { value: &s });, which will make the code compiled

And in this post: What is effect of the " forcing the lifetime to be invariant"? - #19 by zylthinking
Seems the compiler is smart enough and is willing to try its best to make codes compiled.
Then why it does not try harder this time?

use std::marker::PhantomData;

struct Pending<T> {
    phantom: T,
}

fn pending<T>(value: T) -> Pending<T> {
    Pending {
        phantom: T,
    }
}

struct Inspector<'a> {
    value: &'a String,
}

impl Drop for Inspector<'_> {
    fn drop(&mut self) {
        eprintln!("drop inspector {}", self.value)
    }
}

fn main() {
    let p: Pending<Inspector>;
    let s = "hello".to_string();
    p = pending(Inspector { value: &s });
}

NLL tries to reduce lifetimes, but it does not change where things are dropped. If p is declared before s, it will be dropped after, independent of what the borrow checker may decide. In fact, the Drop::drop call counts as another use as far as borrowck is concerned.

5 Likes

Did you mean to have phantom: PhantomData<T>? Then it wouldn't actually call the T = Inspector destructor, and the code is accepted. (And that's the crux of the rust issue you linked, discussing why that is accepted.)

Yes, that maybe the reason.
There are lots of details to figure out, if you don't know them, you can't pridicate the compiler's behavior. Seems no other languages will require that like rust.

Look forward such details can hide behind a uniform & predictable appearance in the future.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.