3.2.8 Drop 中的 Option

08-Drop 中的 Option — Comprehensive Rust

译文 · 基于 Comprehensive Rust

原文链接: https://google.github.io/comprehensive-rust/idiomatic/leveraging-the-type-system/raii/drop_option.html

3.2.8 Drop 中的 Option

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 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<()> {
        // 必须先通过 `Option` 取得 `Handle`
        // 然后才能使用它。
        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 迫使我们处理 Some 和 None 两种情况,即便在逻辑上 None 不可能发生的地方也是如此。Rust 的类型系统无法表达 File 与其 Handle 之间的这种关系,所以我们手动处理两种情况。

深入探索

除了 Option,我们还可以使用 ManuallyDrop, 它通过阻止 Rust 为该值调用 Drop 来抑制自动析构;你必须自己处理拆除。

上一页的 scopeguard 示例 展示了如何用 ManuallyDrop 替代 Option,以避免在值应始终存在的地方处理 None。

在这类设计中,我们通常在 ManuallyDrop<Handle> 旁用单独的标志跟踪 drop 状态,从而知道 handle 是否已被手动消费。

最后修改 August 11, 2026: 更新 (70a5af133)