It seems strange to me, since theoretically it should be possible to statically track whether a variable could exist. If there is any possibility that a variable is uninitialized at one point, the variable could be dropped just before that point since that variable cannot be used anyway.
For the following code:
fn main() {
let mut x: Box<u64>;
if true {
x = Box::new(0);
}
// Point 1
x = Box::new(0);
}
It looks like the compiler is already doing such an analysis, since attempting to execute drop(x); at Point 1 gives error E0381: use of possibly-uninitialized variable. Why not drop variables just before the point where it is possibly uninitialized?
This is an interesting idea, and at least to me, it seems feasible to design it this way.
One disadvantage would be that the order in which things are dropped would depend on later uses. This might lead to surprising, subtle behaviour changes.
This is also the main reason we can't introduce this now: currently, the drop order in Rust is clearly defined, so we are not free to drop earlier without breaking backwards compatibility.
I searched for relevant documents but the drop order seems to only be defined for tuples, structs, slices and Vec. I'm not aware of any documents that define in which order local variables should be dropped. To avoid breaking compatibility, this can be an opt-in, and in case the user wants to explicitly specify the drop order, they can call drop() manually anyway.
I'm not sure what you're suggesting? It's would be unsound to call drop on a variable which is uninitialised, so I don't see how introducing extra drop calls could possibly help, or indeed what problem you're trying to solve?
Note also: drop flags are purely a description of how this might be implemented in the compiler, there's no obligation to actually use anything resembling boolean flags in the compiler, and the way this is implemented has no observable effects on a rust program.
When a variable is possibly-uninitialized, you can’t access it directly, but you can potentially access it through references that were taken at a point where it was known to be initialized. Example:
fn main() {
let mut x: Box<u64>;
let mut y: &u64 = &42;
if true {
x = Box::new(0);
y = &x;
}
// Point 1
println!("{}", *y);
x = Box::new(0);
}
The drop was not actually to drop it, but to trigger a compiler error complaining that the variable is possibly uninitialized. This proves that the compiler does understand whether at each point of execution, a variable was guaranteed to exist or it may be uninitialized.
I'm not solving any specific problem; it is just kind of irritating because dynamic drop flags are a bit ugly in my own opinion (please forget about it). I had expected that Rust was smart enough to track moves statically rather than dynamically, and drops should happen as early as possible. That turns out to be a misunderstanding of the language though.
AFAIK Rust is smart enough to track moves statically whenever possible (and where it can't, I assume LLVM will optimize out redundant drop flags).
That's different from semantics of Drop offered by the language. Rust intentionally guarantees things will be dropped at the end of scope, in a specific order. This allows code such as:
let _lock = lock();
use_stuff_under_a_lock();
// lock is dropped here
With eager drop this would be invalid: (let _ is a special syntax that drops immediately)
let _ = lock();
// lock is dropped here
use_stuff_under_a_lock(); // oops!