Safe pinned-initialization in Rust.
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
One could separate creating and initialization:
but this requires unsafe and is very difficult to use.
The ultimate goal is:
- Safety. We should be able to create and use such pinned type without unsafe. (Obviously the pinned type themselves are still unsafe to implement).
- Ergonomics. The syntax shouldn't be too different from anormal Rust.
- Aggregatable. A struct containing multiple pinned types can be safely created and initialized together.
- 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.
- Fallible. No assumption is made about success of initialization.
This crate provides type
InitResult 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(PinUninit<'a, T>) -> InitResult<'a, T, Err>.
NeedPin::new could be define like this:
With Rust's affine type system and borrow checker, the
essentially a certificate about whether the type is initialized or not.
NeedPin can now be easily initialized:
// In a box let p: = new_box.unwrap; // On the stack init_stack!; let p: = 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.
let p = new_box;
This crate also provides a
UniqueArc, inspired from servo_arc.
They can be used to mutably initialize
Arc before they are being shared.
Arc::pin_with are provided which create
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
#[pin_project] together with your struct, they even share the same
#[pin] field attribute!
See examples for some non-artifical examples.