A Formal Look at Pinning

Do you mean read_ref?

Pin is a reference, so &Pin would be a double-indirection. The expectation is that callers will call borrow on their Pin to reborrow, and hence not lose their original Pin.

There is no *mut, so which pointers are you referring to?

Could you point out which ones? I looked at two and they are both not redundant.

I changed all the parts in the code where I thought a & is enough (instead of a &mut), and marked with // here:

#![feature(pin, arbitrary_self_types, optin_builtin_traits)]

use std::ptr;
use std::mem::Pin;
use std::boxed::PinBox;
use std::marker::Unpin;

struct SelfReferential {
    data: i32,
    self_ref: *const i32,
}
impl !Unpin for SelfReferential {}

impl SelfReferential {
    fn new() -> SelfReferential {
        SelfReferential { data: 42, self_ref: ptr::null()  }
    }

    fn init(mut self: Pin<SelfReferential>) {
        let this : &mut SelfReferential = unsafe { Pin::get_mut(&mut self) };
        // Set up self_ref to point to this.data.
        // here
        this.self_ref = &this.data as *const i32;
    }
    // here
    fn read_ref(self: Pin<SelfReferential>) -> Option<i32> {
        // here
        let this : &SelfReferential = &self;
        // Dereference self_ref if it is non-NULL.
        if this.self_ref == ptr::null() {
            None
        } else {
            Some(unsafe { *this.self_ref })
        }
    }
}

fn main() {
    let mut s = PinBox::new(SelfReferential::new());
    s.as_pin().init();
    println!("{:?}", s.as_pin().read_ref()); // prints Some(42)
}

Good points! I agree this should work. (Unfortunately it’s hard to test that since the API changed a lot.)

However, I think it should be let this : &SelfReferential = &*self;, right?

I think auto-deref should take care of that, but not sure; anyway, this is more explicit, so probably better.

I’m also pretty sure auto-deref should make this work:

fn read_ref(self: Pin<SelfReferential>) -> Option<i32> {
    // Dereference self_ref if it is non-NULL.
    if self.self_ref == ptr::null() {
        None
    } else {
        Some(unsafe { *self.self_ref })
    }
}

but this is even less explicit. On the upside, this does look like a more “natural” way of treating a Pinned, as a reference type.

As an afterthought, couldn’t the signature of this function even have self: &SelfReferential? The guarantee is given when you call init, and isn’t required afterwards (if init was never called, the pointer is null anyway). This does highlight the fact that a Pinned is a promise even beyond its lifetime, but maybe makes the example more complicated rather that simpler.

The API changed, so discussing such details of the old API seems moot at this point.

I suppose one could have an invariant that self_ref will only be non-NULL when the item is pinned. That seems sound, but also (as you say) unnecessarily subtle. Good catch!

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.