Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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) };
    }
}
This slide and its sub-slides should take about 15 minutes. `!Unpin` types can make it difficult to safely implement `Drop`. Implementations must not move pinned values.

Закреплённые типы дают гарантии стабильности памяти. Такие операции, как 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` выполняется без перемещения закреплённого значения