Advanced Lifetimes and Borrowing

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