3.2.5 用 forget 拆除 Drop Bomb

05-用 forget 拆除 Drop Bomb — Comprehensive Rust

译文 · 基于 Comprehensive Rust

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

3.2.5 用 forget 拆除 Drop Bomb

 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
// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0
use std::io::{self, Write};

struct Transaction;

impl Transaction {
    fn start() -> Self {
        Transaction
    }

    fn commit(self) -> io::Result<()> {
        writeln!(io::stdout(), "COMMIT")?;

        // 通过阻止 Drop 运行来拆除 drop bomb。
        std::mem::forget(self);

        Ok(())
    }
}

impl Drop for Transaction {
    fn drop(&mut self) {
        // 这就是「drop bomb」
        panic!("Transaction dropped without commit!");
    }
}

fn main() -> io::Result<()> {
    let tx = Transaction::start();
    // 用 `tx` 构建事务,然后提交。
    // 注释掉对 `commit` 的调用以观察 panic。
    tx.commit()?;
    Ok(())
}

本示例去掉了上一页的标志,让 drop 方法无条件 panic。为在成功提交时避免该 panic,commit 方法现在取得事务的所有权并调用 std::mem::forget, 从而阻止运行 Drop::drop() 方法。

若被 forget 的值拥有本应在其 drop() 实现中释放的堆分配内存,后果之一是内存泄漏。上例中的 Transaction 不属于这种情况,因为它不拥有任何堆内存。

通过战术性地使用 mem::forget(),我们可以避免需要运行时标志。当事务成功提交时,对值调用 std::mem::forget 即可拆除 drop bomb,阻止其 Drop 实现运行。

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