Why NLL fails to notice _b should be dropped before dropping s?

use std::fmt::Display;

fn box_displayable<'a, T: Display + 'a>(t: T) -> Box<dyn Display + 'a>
{
    Box::new(t)
}

fn main() {
    let s = String::from("ss");
    let _b = box_displayable(&s);
    // drop(_b); // fails without this 
    drop(s);
}

Why drop(s) is not allowed? _b does not used any more after all.

I found this can be compiled:

fn main() {
    let s = String::from("ss");
    let _b = Box::new(&s);
    //drop(_b);
    drop(s);
}

Seems the signature tell NLL _b has 'a directly, and 'a lasts until the end of block.
When NLL see a expression, there is no lifetime in signature, it has to do more work by itself and find a more proper lifetime...
While, the following can't be compiled, and 'b can't be told directly.

use std::fmt::Display;

fn box_displayable<'b, 'a : 'b, T: Display + 'a>(t: T) -> Box<dyn Display + 'b>
{
    Box::new(t)
}

fn main() {
    let s = String::from("ss");
    let _b = box_displayable(&s);
    //drop(_b);
    drop(s);
}

NLL doesn't affect drop order. In fact, nothing about lifetime analysis has any impact on drop order. Borrow checking and drop checking are pass/fail; the compiler doesn't use them to figure out when to drop things, only to figure out whether the predetermined drop order leads to valid code.

It's not allowed to drop *_b after s because *_b borrows s and may have drop glue that uses that borrow. The compiler has to account for this possibility because *_b is of type dyn Display + 'a, which could be implemented by a type that has drop glue that uses the borrow.

In the compilable example, *_b is of type &String, which the compiler knows does not have drop glue, so it doesn't have to take the dropping of _b into account when checking whether s is borrowed or can be moved into drop.

Note that in the above explanation, I sometimes used *_b (the referent of _b) and sometimes _b itself. This is deliberate because when it comes to most types (including any types you could write without using experimental features) only the dropness of _b would matter. But Box<T> is special because the compiler knows that dropping it doesn't involve accessing the T (except to call the drop glue for T itself, if any exists).

2 Likes

Thanks, the following code fails to compile, proving what you said is right.

use std::fmt::Display;

fn box_displayable<'b, 'a : 'b, T: Display + 'a>(t: T) -> Box<dyn Display + 'b>
{
    Box::new(t)
}

fn main() {
    let s = String::from("ss");
    let _b = Box::new(&s) as Box<dyn Display>;
    //drop(_b);
    drop(s);
}

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.