split out and rework drop flags section

pull/10/head
Alexis Beingessner 10 years ago committed by Manish Goregaokar
parent 899c8a754a
commit 4a2ba27cba

@ -23,6 +23,7 @@
* [Transmutes](transmutes.md)
* [Uninitialized Memory](uninitialized.md)
* [Checked](checked-uninit.md)
* [Drop Flags](drop-flags.md)
* [Unchecked](unchecked-uninit.md)
* [Ownership-Oriented Resource Management](raii.md)
* [Constructors](constructors.md)

@ -83,27 +83,3 @@ fn main() {
However reassigning `y` in this example *would* require `y` to be marked as
mutable, as a Safe Rust program could observe that the value of `y` changed.
Otherwise the variable is exactly like new.
This raises an interesting question with respect to `Drop`: where does Rust try
to call the destructor of a variable that is conditionally initialized? 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 set and unset. When a variable goes out of scope or is assigned
a value, it evaluates whether the current value of the variable should be dropped.
Of course, static analysis can remove these checks. If the compiler can prove that
a value is guaranteed to be either initialized or not, then it can theoretically
generate more efficient code! As such it may be desirable to structure code to
have *static drop semantics* when possible.
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 language sets the drop flag by
overwriting the entire struct with a particular value. This is pretty obviously
Not The Fastest and causes a bunch of trouble with optimizing code. As such work
is currently under way to move the flags out onto the stack frame where they
more reasonably belong. Unfortunately this work will take some time as it
requires fairly substantial changes to the compiler.
So in general, Rust programs don't need to worry about uninitialized values on
the stack for correctness. Although they might care for performance. Thankfully,
Rust makes it easy to take control here! Uninitialized values are there, and
Safe Rust lets you work with them, but you're never in danger.

@ -0,0 +1,78 @@
% Drop Flags
The examples in the previous section introduce an interesting problem for Rust.
We have seen that'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?
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 effecient code! For instance,
straight-line code has such *static drop semantics*:
```rust
let mut x = Box::new(0); // x was uninit
let mut y = x; // y was uninit
x = Box::new(0); // x was uninit
y = x; // y was init; Drop y!
// y was init; Drop y!
// x was uninit
```
And even branched code where all branches have the same behaviour with respect
to initialization:
```rust
let mut x = Box::new(0); // x was uninit
if condition {
drop(x) // x gets moved out
} else {
println!("{}", x);
drop(x) // x gets moved out
}
x = Box::new(0); // x was uninit
// x was init; Drop x!
```
However code like this *requires* runtime information to correctly Drop:
```rust
let x;
if condition {
x = Box::new(0); // x was uninit
println!("{}", x);
}
// x might be uninit; check the flag!
```
Of course, in this case it's trivial to retrieve static drop semantics:
```rust
if condition {
let x = Box::new(0);
println!("{}", x);
}
```
As of Rust 1.0, the drop flags are actually not-so-secretly stashed in a hidden
field of any type that implements Drop. Rust sets the drop flag by
overwriting the *entire* value with a particular byte. This is pretty obviously
Not The Fastest and causes a bunch of trouble with optimizing code. It's legacy
from a time when you could do much more complex conditional initialization.
As such work is currently under way to move the flags out onto the stack frame
where they more reasonably belong. Unfortunately, this work will take some time
as it requires fairly substantial changes to the compiler.
Regardless, Rust programs don't need to worry about uninitialized values on
the stack for correctness. Although they might care for performance. Thankfully,
Rust makes it easy to take control here! Uninitialized values are there, and
you can work with them in Safe Rust, but you're *never* in danger.
Loading…
Cancel
Save