A simple example:
struct A;
fn main() {
test(2);
test(1);
}
fn test(i: i32) {
println!("test");
let a = A;
if i == 2 {
us(a);
}
println!("end");
}
impl Drop for A {
fn drop(&mut self) {
println!("drop");
}
}
#[allow(unused_variables)]
fn us(a: A){
println!("use");
}
When I run it, the output is:
test
use
drop
end
test
end
drop
I understand in the test(2)
case, a
is moved at us(a)
, so it's output is "test-use-drop-end".
However, in the test(1)
, the output is "test-end-drop", meaning that the compiler knows that a
was not moved.
If us(a)
is called, it will be unnecessary to drop a
in test(i)
, it will be dropped in us(a)
; and if us(a)
is not called, a
must be dropped after println!("end")
.
Since it's impossible for the compiler to know whether us(a)
is called or not, how does compiler know whether a.drop()
shall be called or not after println!("end")
?
This is explained in the Rustnomicon:
As of Rust 1.0, the drop flags are actually not-so-secretly stashed in a hidden field of any type that implements Drop.
The hidden field tells whether the current value has been dropped, or not, and if it has not then it is. Thus, this is known at run-time, and requires a bit of book keeping.
Looking to the future, there is a RFC to remove these hidden fields.
The idea of the RFC is to replace the hidden fields by:
This new strategy has several advantages over the old one:
#[repr(C)]
will now always give a representation equivalent to the C's one even if the struct
implements Drop
struct
size)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With