ManuallyDrop
型について。
ドロップ時のデストラクタの呼出を抑制するラッパー。
型のドロップ時、各フィールドのデストラクタは既定ではフィールドの宣言順で実行される。ここで、各フィールドを ManuallyDrop
型でラップして、Drop::drop
メソッドをオーバーライドすれば、その順序が制御できるようになる。
forget
との違い
ドロップ時のデストラクタの抑制と言えば、forget
関数が連想される。
ただし、forget
関数は引数を消費しつつ、デストラクタは実行しない動作をする。
一方で、ManuallyDrop
型はラップ対象のデストラクタを抑制するだけで、必要ならばデストラクタを手動で実行できる。また、ラップ対象の消費もその後で独立して行える。
以下では、単純にデストラクタの動作を抑制している。
use std::mem::ManuallyDrop;
fn main() {
let dropped = &mut false;
let var = ManuallyDrop::new(Var::new(42, dropped));
assert_eq!(var.value, 42);
consume(var);
assert_eq!(*dropped, false);
}
fn consume<T>(_x: T) {
// nop.
}
struct Var<'a, T> {
value: T,
dropped: &'a mut bool,
}
impl<'a, T> Var<'a, T> {
pub fn new(value: T, dropped: &'a mut bool) -> Self {
Self { value, dropped }
}
}
impl<T> Drop for Var<'_, T> {
fn drop(&mut self) {
*self.dropped = true;
}
}
以下では、MyType
型のドロップ手順を変更している。
mod util;
use std::mem::ManuallyDrop;
use util::*;
fn main() {
let logger = Logger::new();
let var = MyType::new(logger.clone());
drop(var);
assert_eq!(&logger.logs()[0], "[2] is dropped.");
assert_eq!(&logger.logs()[1], "[0] is dropped.");
}
#[allow(dead_code)]
struct MyType {
f0: ManuallyDrop<Val<i32>>,
f1: ManuallyDrop<Val<i32>>,
f2: ManuallyDrop<Val<i32>>,
}
impl MyType {
pub fn new(logger: Logger) -> Self {
Self {
f0: ManuallyDrop::new(Val::new(0, logger.clone())),
f1: ManuallyDrop::new(Val::new(1, logger.clone())),
f2: ManuallyDrop::new(Val::new(2, logger.clone())),
}
}
}
impl Drop for MyType {
fn drop(&mut self) {
unsafe { ManuallyDrop::drop(&mut self.f2) }
unsafe { ManuallyDrop::drop(&mut self.f0) }
}
}
use std::fmt::Display;
use std::rc::Rc;
use std::cell::{Ref, RefCell};
pub struct Val<T: Display> {
value: T,
logger: Logger,
}
impl<T: Display> Val<T> {
pub fn new(value: T, logger: Logger) -> Self {
Self { value, logger }
}
}
impl<T: Display> Drop for Val<T> {
fn drop(&mut self) {
let log = format!("[{}] is dropped.", self.value);
self.logger.log(&log);
}
}
#[derive(Clone)]
pub struct Logger(Rc<RefCell<Vec<String>>>);
impl Logger {
pub fn new() -> Self {
Self(Rc::new(RefCell::new(Vec::new())))
}
pub fn logs(&self) -> Ref<'_, Vec<String>> {
self.0.borrow()
}
pub fn log(&self, text: &str) {
self.0.borrow_mut().push(text.to_string());
}
}