A Formal Look at Pinning


#21

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.


#22

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)
}

#23

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?


#24

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.


#25

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!