The documentation for mem::uninitialized
points out why it is dangerous/unsafe to use that function: calling drop
on uninitialized memory is undefined behavior.
So this code should be, I believe, undefined:
let a: TypeWithDrop = unsafe { mem::uninitialized() };
panic!("=== Testing ==="); // Destructor of `a` will be run (U.B)
However, I wrote this piece of code which works in safe Rust and does not seem to suffer from undefined behavior:
#![feature(conservative_impl_trait)]
trait T {
fn disp(&mut self);
}
struct A;
impl T for A {
fn disp(&mut self) { println!("=== A ==="); }
}
impl Drop for A {
fn drop(&mut self) { println!("Dropping A"); }
}
struct B;
impl T for B {
fn disp(&mut self) { println!("=== B ==="); }
}
impl Drop for B {
fn drop(&mut self) { println!("Dropping B"); }
}
fn foo() -> impl T { return A; }
fn bar() -> impl T { return B; }
fn main() {
let mut a;
let mut b;
let i = 10;
let t: &mut T = if i % 2 == 0 {
a = foo();
&mut a
} else {
b = bar();
&mut b
};
t.disp();
panic!("=== Test ===");
}
It always seems to execute the right destructor, while ignoring the other one. If I tried using a
or b
(like a.disp()
instead of t.disp()
) it correctly errors out saying I might be possibly using uninitialized memory. What surprised me is while panic
king, it always runs the right destructor (printing the expected string) no matter what the value of i
is.
How does this happen? If the runtime can determine which destructor to run, should the part about memory mandatorily needing to be initialized for types with Drop
implemented be removed from documentation of mem::uninitialized()
as linked above?
unsafe
code (which i don't in the main example above) whatever is the behavior observed (even in the first run of it) is pretty much well defined - that would be (one of) the main reason many would choose Rust in the first place (at least i did). – Ventage