Drop: Option
// Copyright 2025 Google LLC // SPDX-License-Identifier: Apache-2.0 struct File(Option<Handle>); impl File { fn open(path: &'static str) -> std::io::Result<Self> { Ok(Self(Some(Handle { path }))) } fn write(&mut self, data: &str) -> std::io::Result<()> { // `Handle` を取得するには、`Option` を経由する必要があります。 // そうして初めて使えるようになります。 let handle = self.0.as_ref().unwrap(); println!("write '{data}' to file '{}'", handle.path); Ok(()) } } impl Drop for File { fn drop(&mut self) { let handle = self.0.take().unwrap(); handle.close(); } } struct Handle { path: &'static str, } impl Handle { fn close(self) { println!("Closing {}", self.path); } } fn main() -> std::io::Result<()> { let mut file = File::open("foo.txt")?; file.write("hello")?; Ok(()) }
-
この例では、
Drop実装の中で内部のHandleに対してcloseを呼び出したいのですが、closeはHandleの所有権を必要とします。通常はこれはできません。なぜなら、dropではFileオブジェクトの所有権を受け取れないため、 フィールドから値をムーブできないからです。 -
HandleをOptionで包むことで、可変参照を通してフィールドから値をムーブ できるようになります。 -
主な欠点は使い勝手です。
Optionにすると、論理的にはNoneが起こりえない場所でも、SomeとNoneの両方のケースを扱わなければなりません。Rust の型システムではFileとそのHandleの間のその関係を表現できないため、 両方のケースを手動で処理します。
さらに調べる
Option の代わりに ManuallyDrop を使うこともできます。これは、Rust が値に対して Drop を呼び出さないようにすることで 自動破棄を抑制するもので、後始末は自分で処理しなければなりません。
前のスライドのscopeguard の例では、 ManuallyDrop で Option を置き換えることで、値が常に存在するはずの場所で None を扱わずに済む方法を示しています。
このような設計では通常、ManuallyDrop<Handle> の隣に別のフラグを置いて 破棄状態を追跡し、それによってハンドルがすでに手動で消費されたかどうかを 把握できます。