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

Пример: реализация Drop для типов !Unpin

// Copyright 2026 Google LLC
// SPDX-License-Identifier: Apache-2.0

use std::cell::RefCell;
use std::marker::PhantomPinned;
use std::mem;
use std::pin::Pin;

thread_local! {
    static BATCH_FOR_PROCESSING: RefCell<Vec<String>> = RefCell::new(Vec::new());
}

#[derive(Debug)]
struct CustomString(String);

#[derive(Debug)]
struct SelfRef {
    data: CustomString,
    ptr: *const CustomString,
    _pin: PhantomPinned,
}

impl SelfRef {
    fn new(data: &str) -> Pin<Box<SelfRef>> {
        let mut boxed = Box::pin(SelfRef {
            data: CustomString(data.to_owned()),
            ptr: std::ptr::null(),
            _pin: PhantomPinned,
        });

        let ptr: *const CustomString = &boxed.data;
        unsafe {
            Pin::get_unchecked_mut(Pin::as_mut(&mut boxed)).ptr = ptr;
        }
        boxed
    }
}

impl Drop for SelfRef {
    fn drop(&mut self) {
        // SAFETY: безопасно, потому что мы читаем байты из String
        let payload = unsafe { std::ptr::read(&self.data) };
        BATCH_FOR_PROCESSING.with(|log| log.borrow_mut().push(payload.0));
    }
}

fn main() {
    let pinned = SelfRef::new("Rust 🦀");
    drop(pinned);

    BATCH_FOR_PROCESSING.with(|batch| {
        println!("Batch: {:?}", batch.borrow());
    });
}

Этот пример использует трейт Drop, чтобы передать данные для постобработки, например телеметрии или логирования.

Комментарий о безопасности неверен. ptr::read создаёт побитовую копию, оставляя self.data в недопустимом состоянии. self.data будет уничтожен снова в конце метода, что приводит к двойному освобождению.

Попросите слушателей исправить код.

Вариант 0: переработать дизайн

Переработайте систему постобработки так, чтобы она работала без Drop.

Вариант 1: Clone

.clone() — очевидный первый выбор, но он выделяет память.

// Copyright 2026 Google LLC
// SPDX-License-Identifier: Apache-2.0

impl Drop for SelfRef {
    fn drop(&mut self) {
        let payload = self.data.0.clone();
        BATCH_FOR_PROCESSING.with(|log| log.borrow_mut().push(payload));
    }
}

Вариант 2: ManuallyDrop

Обёртка CustomString в ManuallyDrop предотвращает второе автоматическое уничтожение в конце реализации Drop.

// Copyright 2026 Google LLC
// SPDX-License-Identifier: Apache-2.0

struct SelfRef {
    data: ManuallyDrop<CustomString>,
    ptr: *const CustomString,
    _pin: PhantomPinned,
}

// ...

impl Drop for SelfRef {
    fn drop(&mut self) {
        // SAFETY: self.data
        let payload = unsafe { ManuallyDrop::take(&mut self.data) };
        BATCH_FOR_PROCESSING.with(|log| log.borrow_mut().push(payload.0));
    }
}