Пример: реализация 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));
}
}