Pin<Ptr> и Drop
Ключевая сложность закреплённых типов !Unpin — реализация трейта Drop. Метод drop принимает &mut self, что позволяет переместить значение. Однако закреплённые значения перемещать нельзя.
Неправильная реализация Drop
Легко случайно переместить значение внутри drop. Такие операции, как присваивание, ptr::read и mem::replace, могут незаметно нарушить гарантию pinning.
// 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` уничтожается в конце функции, происходит двойное освобождение!
let _dupe = unsafe { std::ptr::read(&self.data) };
}
}
Закреплённые типы дают гарантии стабильности памяти. Такие операции, как ptr::read и mem::replace, могут незаметно нарушить эти гарантии, перемещая или дублируя данные и делая недействительными внутренние указатели без ведома системы типов.
В этом методе drop() _dupe — побитовая копия self.data. В конце метода он будет уничтожен вместе с self. Это двойное уничтожение — неопределённое поведение.
Правильная реализация Drop
Чтобы правильно реализовать Drop для типа !Unpin, нужно гарантировать, что значение не перемещается. Распространённый приём — создать вспомогательную функцию, которая работает с Pin<&mut T>.
// 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` выполняется без перемещения закреплённого значения