PhantomPinned
std::marker::PhantomPinned is a zero-sized marker struct in the standard library that explicitly opts out of the Unpin auto-trait (impl !Unpin for PhantomPinned {}). Including a PhantomPinned field inside a custom struct makes the enclosing struct !Unpin, preventing safe code from calling Pin::new(&mut val) or obtaining &mut Self from Pin<&mut Self>.
Memory Footprint
Like PhantomData<T>, PhantomPinned is a zero-sized type (size_of::<PhantomPinned>() == 0 and align_of::<PhantomPinned>() == 1). Adding _pin: PhantomPinned to a struct adds 0 bytes to the struct's memory layout.
Compiler Error When Attempting Pin::new on a PhantomPinned Type (E0277)
Once a struct includes PhantomPinned, attempting to pass &mut Self to Pin::new or unwrapping Pin<&mut Self> via DerefMut fails at compile time with E0277:
// ❌ does not compile
use std::marker::PhantomPinned;
use std::pin::Pin;
struct PinnedNode {
data: u64,
_pin: PhantomPinned,
}
fn main() {
let mut node = PinnedNode {
data: 1,
_pin: PhantomPinned,
};
let pinned = Pin::new(&mut node);
}
error[E0277]: `PhantomPinned` cannot be unpinned
--> src/main.rs:15:27
|
15 | let pinned = Pin::new(&mut node);
| -------- ^^^^^^^^^ within `PinnedNode`, the trait `Unpin` is not implemented for `PhantomPinned`
| |
| required by a bound introduced by this call
|
= note: consider using the `pin!` macro
consider using `Box::pin` if you need to access the pinned value outside of the current scope
note: required because it appears within the type `PinnedNode`
--> src/main.rs:5:8
|
5 | struct PinnedNode {
| ^^^^^^^^^^
Building a Sound Self-Referential Struct with PhantomPinned
By combining PhantomPinned with Box::pin (or std::pin::pin!), a struct can safely store a raw pointer to one of its own fields after being pinned, knowing that its memory address can never change prior to Drop:
use std::marker::PhantomPinned;
use std::pin::Pin;
use std::ptr;
struct SelfReferentialBuffer {
text: String,
// Points directly to `self.text` on the stack/heap once initialized:
self_text_ptr: *const String,
_pin: PhantomPinned,
}
impl SelfReferentialBuffer {
fn new(content: &str) -> Pin<Box<Self>> {
let mut boxed = Box::pin(Self {
text: content.to_string(),
self_text_ptr: ptr::null(),
_pin: PhantomPinned,
});
// Establish the self-pointer now that the struct is pinned on the heap:
let text_addr: *const String = &raw const boxed.text;
// SAFETY: Setting `self_text_ptr` does not move the pinned struct.
unsafe {
let mut_ref: Pin<&mut Self> = boxed.as_mut();
mut_ref.get_unchecked_mut().self_text_ptr = text_addr;
}
boxed
}
fn text_via_self_ptr(self: Pin<&Self>) -> &str {
// SAFETY: `self_text_ptr` was initialized in `new()` and `self` is pinned,
// so `self.text` has remained at the exact same address `self_text_ptr`.
unsafe { (*self.self_text_ptr).as_str() }
}
}
fn main() {
let pinned_buf = SelfReferentialBuffer::new("pinned self-reference");
// Moving the `Pin<Box<...>>` handle to a new variable moves only the 8-byte
// heap pointer; the heap-allocated `SelfReferentialBuffer` stays at the same address!
let moved_handle = pinned_buf;
println!(
"read via internal self pointer: {:?}",
moved_handle.as_ref().text_via_self_ptr()
);
println!(
"field addr == self_text_ptr: {}",
(&raw const moved_handle.text) == moved_handle.self_text_ptr
);
}
read via internal self pointer: "pinned self-reference"
field addr == self_text_ptr: true