Why Drop trait is only executed at the end of the scope, instead of after the last use?

This is a question from rust onomicon # lifetime

The first example can compile, as x is a reference and the compiler can infer its lifetime as minimal as the last use here :println!(), so x is dropped after this line.

let mut data = vec![1, 2, 3];
let x = &data[0];
println!("{}", x);
// This is OK, x is no longer needed
data.push(4);

But the case is different when x is a struct implemented Drop trait.

#[derive(Debug)]
struct X<'a>(&'a i32);

impl Drop for X<'_> {
    fn drop(&mut self) {}
}

let mut data = vec![1, 2, 3];
let x = X(&data[0]);
println!("{:?}", x);
data.push(4);
// Here, the destructor is run and therefore this'll fail to compile.

The onomicon says in this case, drop() is only executed at the very end of a scope, so x keeps valid until the last line.

But why the compiler cannot minimize the lifetime of x to the last use? And is applying drop() just after the last use has some nontrivial side effects when x is implemented Drop trait?

>Solution :

The primary reason is that it was once defined to be like that, and now changing it isn’t possible any more because it wouldn’t be backwards-compatible and might break stuff.

Your code is easily fixable by introducing a nested scope, though, which is (to my understanding) best practices in those situations:

#[derive(Debug)]
struct X<'a>(&'a i32);

impl Drop for X<'_> {
    fn drop(&mut self) {}
}

fn main() {
    let mut data = vec![1, 2, 3];
    {
        let x = X(&data[0]);
        println!("{:?}", x);
    }
    data.push(4);
}
X(1)

Alternatively, you could drop it manually:

#[derive(Debug)]
struct X<'a>(&'a i32);

impl Drop for X<'_> {
    fn drop(&mut self) {}
}

fn main() {
    let mut data = vec![1, 2, 3];

    let x = X(&data[0]);
    println!("{:?}", x);
    drop(x);

    data.push(4);
}
X(1)

Leave a Reply