ManuallyDrop 型について。

概要

ドロップ時のデストラクタの呼出を抑制するラッパー。

よくある用法

型のドロップ時、各フィールドのデストラクタは既定ではフィールドの宣言順で実行される。ここで、各フィールドを ManuallyDrop 型でラップして、Drop::drop メソッドをオーバーライドすれば、その順序が制御できるようになる。

forget との違い

ドロップ時のデストラクタの抑制と言えば、forget 関数が連想される。

ただし、forget 関数は引数を消費しつつ、デストラクタは実行しない動作をする。

一方で、ManuallyDrop 型はラップ対象のデストラクタを抑制するだけで、必要ならばデストラクタを手動で実行できる。また、ラップ対象の消費もその後で独立して行える。

サンプル 1

動作の確認

以下では、単純にデストラクタの動作を抑制している。


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;
    }
}

サンプル 2

よくある用法

以下では、MyType 型のドロップ手順を変更している。

main.rs

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) }
    }
}
util.rs

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());
    }
}