Pin<Ptr> と Drop
ピン留めされた !Unpin 型における重要な課題の 1 つは、Drop トレイトを実装することです。 drop メソッドは &mut self を受け取るため、値をムーブできてしまいます。しかし、 ピン留めされた値はムーブしてはなりません。
誤った Drop 実装
drop の中で誤って値をムーブしてしまうのは簡単です。代入、 ptr::read、mem::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::read や mem::replace のような操作は、データをムーブまたは複製することで、 型システムに認識されないまま内部ポインタを無効にし、これらの保証を 気付かないうちに破る可能性があります。
この drop() メソッドでは、_dupe は self.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` が実行されます