Theme
Q029. What are drop flags and why does the compiler need them?
Short Answer
Drop flags represent runtime initialization state when control flow prevents rustc from deciding statically whether a value still exists. Initialization sets the relevant state, while a move or drop clears it; cleanup checks it before running a destructor. Rust does not attach a flag to every value, and optimization often removes or merges flags that are unnecessary.
Deep Dive
Consider a value moved only when a runtime condition is true. At scope exit, the compiler must drop it on the path where it remains initialized and skip it on the path where ownership moved elsewhere.
Drop flags solve this without zeroing moved-from storage. Conceptually:
text
initialize value -> flag = true
move value -> flag = false
drop point -> if flag { run drop glue }Partial initialization may require state for individual move paths or fields. However, rustc uses dataflow analysis to classify drops as always live, always dead, conditional, or partially initialized, adding dynamic state only where static analysis is insufficient.
Internal Model
MIR initially contains imprecise drop terminators. Drop elaboration combines MaybeInitializedPlaces and MaybeUninitializedPlaces analyses, removes dead drops, preserves static drops, and expands conditional or open drops into guarded cleanup.
The final machine code may reuse an existing branch condition instead of materializing a separate boolean. Drop flags are a semantic compiler model, not a guaranteed memory field beside the value.
Example
rust
fn consume(message: String) {
println!("sent: {message}");
}
fn route(should_send: bool) {
let message = String::from("event");
if should_send {
consume(message);
}
} // drop message only when it was not moved
fn main() {
route(true);
route(false);
}Common Mistakes
- Imagining a permanent
is_movedfield stored beside every Rust value. - Assuming moved-from bytes must be zeroed to prevent double drop.
- Forgetting that optimization can remove explicit flag storage and branches.
Follow-ups
- What is an open drop in MIR?
- How do partial moves create separate drop obligations?
- When can the compiler prove that a drop is always dead?
Related
- Q008. How is ownership enforced and represented under the hood?
- Q017. How does ownership flow through branches, loops, and conditional initialization?
- Q028. What is drop glue?
References
- Rust Reference
- rustc-dev-guide