Drop Flags
The examples in the previous section introduce an interesting problem for Rust. We have seen that it's possible to conditionally initialize, deinitialize, and reinitialize locations of memory totally safely. For Copy types, this isn't particularly notable since they're just a random pile of bits. However types with destructors are a different story: Rust needs to know whether to call a destructor whenever a variable is assigned to, or a variable goes out of scope. How can it do this with conditional initialization?
Note that this is not a problem that all assignments need worry about. In
particular, assigning through a dereference unconditionally drops, and assigning
in a let
unconditionally doesn't drop:
This is only a problem when overwriting a previously initialized variable or one of its subfields.
It turns out that Rust actually tracks whether a type should be dropped or not at runtime. As a variable becomes initialized and uninitialized, a drop flag for that variable is toggled. When a variable might need to be dropped, this flag is evaluated to determine if it should be dropped.
Of course, it is often the case that a value's initialization state can be statically known at every point in the program. If this is the case, then the compiler can theoretically generate more efficient code! For instance, straight- line code has such static drop semantics:
Similarly, branched code where all branches have the same behavior with respect to initialization has static drop semantics:
However code like this requires runtime information to correctly Drop:
Of course, in this case it's trivial to retrieve static drop semantics:
The drop flags are tracked on the stack.
In old Rust versions, drop flags were stashed in a hidden field of types that implement Drop
.