Pin<Ptr>Drop

ピン留めされた !Unpin 型における重要な課題の 1 つは、Drop トレイトを実装することです。 drop メソッドは &mut self を受け取るため、値をムーブできてしまいます。しかし、 ピン留めされた値はムーブしてはなりません。

誤った Drop 実装

drop の中で誤って値をムーブしてしまうのは簡単です。代入、 ptr::readmem::replace のような操作は、気付かないうちにピン留めの 保証を破る可能性があります。

// 著作権 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` がドロップされると、二重解放になります!
        let _dupe = unsafe { std::ptr::read(&self.data) };
    }
}
`!Unpin` 型では、`Drop` を安全に実装することが難しくなる場合があります。実装では、 ピン留めされた値をムーブしてはなりません。

ピン留めされた型は、メモリの安定性について保証を提供します。ptr::readmem::replace のような操作は、データをムーブまたは複製することで、 型システムに認識されないまま内部ポインタを無効にし、これらの保証を 気付かないうちに破る可能性があります。

この drop() メソッドでは、_dupeself.data のビット単位のコピーです。メソッドの最後で、 これは self とともにドロップされます。この二重ドロップは未定義動作です。

正しい Drop 実装

!Unpin 型に対して Drop を正しく実装するには、値がムーブされないことを 保証しなければなりません。一般的なパターンは、Pin<&mut T> に対して 操作するヘルパー関数を作ることです。

// 著作権 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` はその値が使われる最後の機会なので、`drop_pinned` を
        // 安全に呼び出せます。`self` がこの後ムーブされないことが分かって
        // いるため、`new_unchecked` を使います。
        unsafe {
            SelfRef::drop_pinned(Pin::new_unchecked(self));
        }
    }
}

fn main() {
    let _pinned = SelfRef::new("Hello, ");
} // ピン留めされた値をムーブせずに `Drop` が実行されます