Pin::set is safe. Why?

the Pin::set is safe. Why?

#![feature(debug_closure_helpers)]

use std::fmt::Debug;
use std::marker::PhantomPinned;
use std::mem;
use std::pin::Pin;
use std::ptr::NonNull;

#[repr(C)]
struct PinType {
    name: String,
    t: Option<NonNull<Self>>,
    _marker: PhantomPinned
}

impl Debug for PinType{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("PinType")
            .field_with("self", |f| {
                f.write_fmt(format_args!("(addr:{:p}, size:{}, t: {:p})", self, mem::size_of::<Self>(), &self.t))
            })
            .field("self-ptr", &self.t)
            .field("name", &self.name)
            .finish()
    }
}

fn main() {
    let mut pin_type = PinType {
        name: "PinType".to_string(),
        t: None,
        _marker: PhantomPinned
    };
    pin_type.t = Some(NonNull::from(&pin_type));
    unsafe { println!("{:?}", &pin_type); }

    let mut pin = unsafe { Pin::new_unchecked(&mut pin_type) };

    let mut pin_type2 = PinType {
        name: "PinType2222".to_string(),
        t: None,
        _marker: PhantomPinned
    };
    pin_type2.t = Some(NonNull::from(&pin_type2));

    // pin.t is address of pin_type2. at the meantime, pin.t --> pin_type2, pin_type2.name and pin.name are same.
    // At api docs, the function is safe. I don't understand why it is safe.
    // Why it not break pinning invariant?
    //      PinType { self: (addr:0x7ff7bcfd7810, size:32, t: 0x7ff7bcfd7828), self-ptr: Some(0x7ff7bcfd7810), name: "PinType" }
    //      PinType { self: (addr:0x7ff7bcfd7810, size:32, t: 0x7ff7bcfd7828), self-ptr: Some(0x7ff7bcfd78b8), name: "PinType2222" }
    pin.set(pin_type2);
    unsafe { println!("{:?}", pin); }
}

Have you read the link to Pin document in you link?

Direct quote

The purpose of pinning is not just to prevent a value from being moved , but more generally to be able to rely on the pinned value remaining valid at a specific place in memory.
...
From the moment a value is pinned by constructing a Pinning pointer to it, that value must remain, valid, at that same address in memory, until its drop handler is called.

And from Pin::set doc itself:

This overwrites pinned data, but that is okay: the original pinned value’s destructor gets run before being overwritten and the new value is also a valid value of the same type, so no pinning invariant is violated.

As for pin.t points to pin itself, that's an invariant of PinType you are meant to preserve manually. Pin is not responsible for that.

Do note that Pin has no indirection by itself. So normally speaking, it's supposed to wrap a pointer like object, like Pin<Box<T>> or Pin<&mut T>.

Pin is a wrapper around some kind of pointer Ptr which makes that pointer “pin” its pointee value in place, thus preventing the value referenced by that pointer from being moved or otherwise invalidated at that place in memory unless it implements Unpin.

Loosely speaking, The purpose of Pin is preserving T being moved in Pin<Ptr<T>>, instead of T in Pin<T>.

5 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.