summaryrefslogtreecommitdiffstats
path: root/src/doc/nomicon/src/drop-flags.md
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/doc/nomicon/src/drop-flags.md83
1 files changed, 83 insertions, 0 deletions
diff --git a/src/doc/nomicon/src/drop-flags.md b/src/doc/nomicon/src/drop-flags.md
new file mode 100644
index 000000000..6a0236a0d
--- /dev/null
+++ b/src/doc/nomicon/src/drop-flags.md
@@ -0,0 +1,83 @@
+# 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:
+
+```rust
+let mut x = Box::new(0); // let makes a fresh variable, so never need to drop
+let y = &mut x;
+*y = Box::new(1); // Deref assumes the referent is initialized, so always drops
+```
+
+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*:
+
+```rust
+let mut x = Box::new(0); // x was uninit; just overwrite.
+let mut y = x; // y was uninit; just overwrite and make x uninit.
+x = Box::new(0); // x was uninit; just overwrite.
+y = x; // y was init; Drop y, overwrite it, and make x uninit!
+ // y goes out of scope; y was init; Drop y!
+ // x goes out of scope; x was uninit; do nothing.
+```
+
+Similarly, branched code where all branches have the same behavior with respect
+to initialization has static drop semantics:
+
+```rust
+# let condition = true;
+let mut x = Box::new(0); // x was uninit; just overwrite.
+if condition {
+ drop(x) // x gets moved out; make x uninit.
+} else {
+ println!("{}", x);
+ drop(x) // x gets moved out; make x uninit.
+}
+x = Box::new(0); // x was uninit; just overwrite.
+ // x goes out of scope; x was init; Drop x!
+```
+
+However code like this *requires* runtime information to correctly Drop:
+
+```rust
+# let condition = true;
+let x;
+if condition {
+ x = Box::new(0); // x was uninit; just overwrite.
+ println!("{}", x);
+}
+ // x goes out of scope; x might be uninit;
+ // check the flag!
+```
+
+Of course, in this case it's trivial to retrieve static drop semantics:
+
+```rust
+# let condition = true;
+if condition {
+ let x = Box::new(0);
+ println!("{}", x);
+}
+```
+
+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`.