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?
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 practice 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)