src/unsafe-deep-dive/pinning/pin-and-drop.md
A key challenge with pinned, !Unpin types is implementing the Drop trait.
The drop method takes &mut self, which allows moving the value. However,
pinned values must not be moved.
Drop ImplementationIt's easy to accidentally move a value inside drop. Operations like
assignment, ptr::read, and mem::replace can silently break the pinning
guarantee.
# // 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) {
// BAD: `ptr::read` moves `self.data` out of `self`.
// When `_dupe` is dropped at the end of the function, it's a double free!
let _dupe = unsafe { std::ptr::read(&self.data) };
}
}
Pinned types make guarantees about memory stability. Operations like ptr::read
and mem::replace can silently break these guarantees by moving or duplicating
data, invalidating internal pointers without the type system's knowledge.
In this drop() method, _dupe is a bitwise copy of self.data. At the end of
the method, it will be dropped along with self. This double drop is undefined
behavior.
Drop ImplementationTo implement Drop correctly for a !Unpin type, you must ensure that the
value is not moved. A common pattern is to create a helper function that
operates on 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` is pinned before we create the self-reference.
unsafe {
Pin::as_mut(&mut this).get_unchecked_mut().ptr = ptr;
}
this
}
// This function can only be called on a pinned `SelfRef`.
unsafe fn drop_pinned(self: Pin<&mut SelfRef>) {
// `self` is pinned, so we must not move out of it.
println!("dropping {}", self.data);
}
}
impl Drop for SelfRef {
fn drop(&mut self) {
// We can safely call `drop_pinned` because `drop` is the last time
// the value is used. We use `new_unchecked` because we know `self`
// will not be moved again.
unsafe {
SelfRef::drop_pinned(Pin::new_unchecked(self));
}
}
}
fn main() {
let _pinned = SelfRef::new("Hello, ");
} // `Drop` runs without moving the pinned value