Rustのstd::mem::forgetがunsafeでない理由
Rustのstd::mem::forget
はunsafeではない。このことはAPIドキュメントに説明されている。Rustのオブジェクトは二重dropしてはいけないが、dropをせずに放置する分には(少なくともメモリ保護の観点からは)安全であり、 Drop
を実装する側は drop
が呼ばれなくても安全であるようにしなければならない。
これは、同じくドキュメントで説明されているように、容易に正当化できる。以下のように Rc
で循環参照を作ると、 forget
を模倣することができる。(std::mem::forget
とは異なり、より多くのメモリリークを生じるという違いはある)
pub fn my_forget<T>(x: T) { use std::rc::Rc; use std::cell::RefCell; struct Cyc<T> { x: T, cyc: RefCell<Option<Rc<Cyc<T>>>>, } let c = Rc::new(Cyc { x: x, cyc: RefCell::new(None) }); *c.cyc.borrow_mut() = Some(c.clone()); } struct A<'a> { s: &'a str, } impl<'a> A<'a> { pub fn new(s: &'a str) -> Self { A { s: s, } } } impl<'a> Drop for A<'a> { fn drop(&mut self) { println!("dropped: {}", self.s); } } fn main() { let data1 = [49]; let obj1 = A::new(std::str::from_utf8(&data1).unwrap()); let data2 = [50]; let obj2 = A::new(std::str::from_utf8(&data2).unwrap()); std::mem::forget(obj2); let data3 = [51]; let obj3 = A::new(std::str::from_utf8(&data3).unwrap()); my_forget(obj3); }
やや直感に反する点として、 forget<T>
は T: 'static
を要求していないという点がある。例えば上の例でも、スタック上の文字列へのポインタが、循環参照の中に放置されたまま、プログラムが終了してしまっている。これはRustでは許されているようだ。