8.8 `Pin<Ptr>` 与 `Drop`

Pin<Ptr> 与 Drop — Comprehensive Rust

译文 · 基于 Comprehensive Rust

原文链接: https://google.github.io/comprehensive-rust/unsafe-deep-dive/pinning/pin-and-drop.html

8.8 Pin<Ptr> 与 Drop

对已固定、!Unpin 类型而言,实现 Drop trait 是一个关键挑战。drop 方法接受 &mut self,这允许移动该值。然而,被固定的值绝不能移动。

错误的 Drop 实现

在 drop 内很容易意外移动值。赋值、ptr::read 和 mem::replace 等操作会在不知不觉中破坏 pinning 保证。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Copyright 2026 Google LLC
// SPDX-License-Identifier: Apache-2.0
struct SelfRef {
    data: String,
    ptr: *const String,
}

impl Drop for SelfRef {
    fn drop(&mut self) {
        // 错误:`ptr::read` 将 `self.data` 移出 `self`。
        // 当 `_dupe` 在函数结束时被 drop,会导致 double free!
        let _dupe = unsafe { std::ptr::read(&self.data) };
    }
}

!Unpin 类型可能使安全实现 Drop 变得困难。实现绝不能移动已固定的值。

已固定类型对内存稳定性做出保证。ptr::read 和 mem::replace 等操作会通过移动或复制数据在不知不觉中破坏这些保证,在类型系统不知情的情况下使内部指针失效。

在此 drop() 方法中,_dupe 是 self.data 的按位拷贝。方法结束时,它会与 self 一起被 drop。这种 double drop 是未定义行为。

正确的 Drop 实现

要为 !Unpin 类型正确实现 Drop,必须确保值不被移动。常见模式是创建一个在 Pin<&mut T> 上操作的辅助函数。

 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
41
42
43
44
45
46
// Copyright 2026 Google LLC
// SPDX-License-Identifier: Apache-2.0
use std::marker::PhantomPinned;
use std::pin::Pin;

struct SelfRef {
    data: String,
    ptr: *const String,
    _pin: PhantomPinned,
}

impl SelfRef {
    fn new(data: impl Into<String>) -> Pin<Box<SelfRef>> {
        let mut this = Box::pin(SelfRef {
            data: data.into(),
            ptr: std::ptr::null(),
            _pin: PhantomPinned,
        });
        let ptr: *const String = &this.data;
        // SAFETY: 在创建自引用之前,`this` 已被固定。
        unsafe {
            Pin::as_mut(&mut this).get_unchecked_mut().ptr = ptr;
        }
        this
    }

    // 此函数只能在已固定的 `SelfRef` 上调用。
    unsafe fn drop_pinned(self: Pin<&mut SelfRef>) {
        // `self` 已被固定,因此不能从中移出。
        println!("dropping {}", self.data);
    }
}

impl Drop for SelfRef {
    fn drop(&mut self) {
        // 可以安全调用 `drop_pinned`,因为 `drop` 是该值最后一次被使用。
        // 我们使用 `new_unchecked`,因为我们知道 `self` 不会再被移动。
        unsafe {
            SelfRef::drop_pinned(Pin::new_unchecked(self));
        }
    }
}

fn main() {
    let _pinned = SelfRef::new("Hello, ");
} // `Drop` 运行时不移动已固定的值

8.8.1 完整示例

01-完整示例 — Comprehensive Rust

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