Crate pin_init[][src]

Expand description

Safe pinned-initialization in Rust.

The problem

Rust’s Pin provides sufficient guarantee for C interop and self-referential structs – their address are stable once they’re pinned and the destructor is guaranteed to be called before the memory region can be deallocated.

The problem here is “once pinned”. Pin expects the type can be created without pinning, and can be pinned later before any operations. This is okay for Generators, which are created without any self references, and self references can only be created when polling the generator. For other types, e.g. pthread_mutex_t, it is expected to be pinned from the start.

For demonstration purpose, we will use this type NeedPin:

struct NeedPin {
    // Must points to itself
    address: *const NeedPin,
    _pinned: PhantomPinned,
}

impl NeedPin {
    fn verify(&self) {
        assert!(ptr::eq(self, self.address), "invariant not held");
    }
}

impl Drop for NeedPin {
    fn drop(&mut self) {
        /* Must be called */
    }
}

One could separate creating and initialization (Infallible is used as a placeholder here but in reality it can fail):

impl NeedPin {
    unsafe fn uninit() -> Self {
        Self {
            address: ptr::null(),
            _pinned: PhantomPinned,
        }
    }

    unsafe fn init(self: Pin<&mut Self>) -> Result<(), Infallible> {
        let this = unsafe { self.get_unchecked_mut() };
        this.address = this;
        Ok(())
    }
}

but this requires unsafe and is very difficult to use.

The ultimate goal is:

  1. Safety. We should be able to create and use such pinned type without unsafe. (Obviously the pinned type themselves are still unsafe to implement).
  2. Ergonomics. The syntax shouldn’t be too different from anormal Rust.
  3. Aggregatable. A struct containing multiple pinned types can be safely created and initialized together.
  4. No Implicit Allocation. Allocation should not be required during initialization. User should be able to dictate whether it’s initialized in a box or on the stack.
  5. Fallible. No assumption is made about success of initialization.

The solution: pin_init

This crate provides type PinInit and PinInitResult as the primitives for safe pinned-initialization. Details about these types can be found in their respective documentation, but in a nutshell, instead of having a (fallible) constructor of type FnOnce() -> Result<T, Err> like a normal unpinned type, pin_init expect you to present a constructor of type for<'a> FnOnce(PinInit<'a, T>) -> PinInitResult<'a, T, Err>.

NeedPin::new could be define like this:

impl NeedPin {
    pub fn new(mut this: PinInit<'_, Self>) -> PinInitResult<'_, Self, Infallible> {
        let v = this.get_mut().as_mut_ptr();
        unsafe { *ptr::addr_of_mut!((*v).address) = v };
        Ok(unsafe { this.init_ok() })
    }
}

With Rust’s affine type system and borrow checker, the PinInitResult is essentially a certificate about whether the type is initialized or not. NeedPin can now be easily initialized:

// In a box
let p: Pin<Box<NeedPin>> = pin_init::new_box(NeedPin::new).unwrap();
// On the stack
init_stack!(p = NeedPin::new);
let p: Pin<&mut NeedPin> = p.unwrap();

For structs, if #[pin_init] when defining the struct, then init_pin! can create it very similar to the struct expression. Nested structures are also supported.

#[pin_init]
struct ManyPin {
    #[pin]
    a: NeedPin,
    b: usize,
}

#[pin_init]
struct TooManyPin {
    #[pin]
    a: NeedPin,
    #[pin]
    b: ManyPin,
}
let p = new_box(init_pin!(TooManyPin {
    a: NeedPin::new,
    b: init_pin!(ManyPin {
        a: NeedPin::new,
        b: 0,
    }),
}));

This crate also provides a UniqueRc and UniqueArc, inspired from servo_arc. They can be used to mutably initialize Rc and Arc before they are being shared. new_rc and new_arc are provided which create UniqueRc and UniqueArc internally, pin-initialize it with given constructor, and convert them to the shareable form.

This crate allows safe initialization of pinned data structure. pin-project can be used to safely access these structs. You can use both #[pin_init] and #[pin_project] together with your struct, they even share the same #[pin] field attribute!

See examples for some non-artifical examples.

Macros

init_pin

Create and pin-initialize a struct.

init_stack

Create and pin-initialize a new variable on the stack.

Structs

PinInit

A pinned, uninitialized pointer.

PinInitErr

Proof that the value is not pin-initialized.

PinInitOk

Proof that the value is pin-initialization.

UniqueArcalloc

An uniquely owned Arc.

UniqueRcalloc

An uniquely owned Rc.

Traits

PinInitable

Types that can be constructed using init_pin.

Functions

init_boxalloc

Pin-initialize a box.

init_unique_arcalloc

Pin-initialize a UniqueArc.

init_unique_rcalloc

Pin-initialize a UniqueRc.

new_arcalloc

Create a new Arc and pin-initialize it.

new_boxalloc

Create a new Box and pin-initialize it.

new_rcalloc

Create a new Rc and pin-initialize it.

new_unique_arcalloc

Create a new UniqueArc and pin-initialize it.

new_unique_rcalloc

Create a new UniqueRc and pin-initialize it.

Type Definitions

PinInitResult

Result of pin-initialization.

Attribute Macros

pin_init

Mark a type as being init_pin!-able.