В документации для mem::uninitialized указывается, почему это опасно/небезопасно использовать эту функцию: вызов drop в неинициализированной памяти - это поведение undefined.
Итак, этот код должен быть, я считаю, undefined:
let a: TypeWithDrop = unsafe { mem::uninitialized() };
panic!("=== Testing ==="); // Destructor of `a` will be run (U.B)
Однако я написал этот фрагмент кода, который работает в безопасном Rust и, похоже, не страдает от поведения undefined:
#![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 ===");
}
Кажется, что он выполняет правильный деструктор, игнорируя другой. Если я попытался использовать a или b (например, a.disp() вместо t.disp()), он правильно ошибается, говоря, что, возможно, я могу использовать неинициализированную память. Что меня удивило, когда король panic, он всегда запускает правильный деструктор (печатает ожидаемую строку) независимо от того, что такое значение i.
Как это происходит? Если среда выполнения может определить, какой деструктор будет выполняться, должна ли быть удалена часть из памяти, которая должна быть инициализирована для типов с drop, из документации mem::uninitialized(), как указано выше?