Crate owned_pin

source ·
Expand description

Types that own and pin data to its location in memory.

While the Pin<P> wrapper in Rust’s standard library enables pinning data in memory, it has no guarantee of “owning” the pinned data, which prevents the usage to semantically-but-not-concerning-memory moving the ownership of the pinned data:

use core::pin::{Pin, pin};
use core::marker::PhantomPinned;

fn try_to_take_the_ownership_of<T>(pinned: Pin<&mut T>) {}

let mut value = pin!(PhantomPinned);
// The caller can reborrow the value...
try_to_take_the_ownership_of(value.as_mut());
// ... so the same pinned data can be used twice,
// thus unable to guarantee the move semantics.
try_to_take_the_ownership_of(value.as_mut());

Function implementors may use unsafe keyword to state the contract of moving the ownership of the data. However, this remedy significantly increases the difficulty of maintaining the code.

In practice, this is because there is no such smart pointer that owns data by holding a unique reference to some other location on the stack.

Thus, we introduce the OnStack<T> smart pointer, which does the work mentioned above in a memory-safe way, and a type alias OPin<T> = Pin<OnStack<T>>, which both enable the example above to work as desired:

use owned_pin::{OPin, opin};
use core::marker::PhantomPinned;

// `OPin<'a, T>` equals to `Pin<OnStack<'a, T>>`.
fn take_the_ownership_of<T>(owned_and_pinned: OPin<T>) {}

let value = opin!(PhantomPinned);
// The `as_mut` method of `OPin` actually returns `Pin<&mut T>`...
take_the_ownership_of(value);
// ... so the value itself cannot be used again.
// The line below causes rustc to emit `E0382`.
take_the_ownership_of(value);

Unpin

Pin<OnStack<T>>’s moving semantics provides it with more functionality than an ordinary Pin<&mut T>. For example, owning an Unpinned data means we can move out the raw data from this wrapper:

use owned_pin::{OPin, opin, unpin};

fn take_the_ownership_of(pointer: OPin<String>) {
    // Wow! the unpinned ownership is now ours!
    let string: String = unpin(pointer);
    println!("{string}");
}

take_the_ownership_of(opin!(String::from("Hello")));

This magic works safe and sound thanks to the full potential of ManuallyDrop, implying that the wrapped pointer actually points to a ManuallyDrop<T>.

Note that Pin<Box<T>> does all the things above as good as OPin<T>, and the former can even be 'static if T is 'static. Nevertheless, additional heap memory is consumed the achieve the result, which is unrecommendable in memory-constraint scenarios.

Relationship with R-value references in C++

This crate is in fact inspired by R-value references in C++, and OPin<T> behaves more similar to R-value references than the original move semantics in Rust and the Pin<&mut T> wrapper. However, there is still no “default value” left when some data is OPinned or moved out, guaranteeing the memory safety requirements of Rust.

The more detailed comparison is yet to be discussed.

Utilities

Beside introducing the new OnStack type, we have also built some basic data structures that can be pinned onto the stack, demonstrating some of its potential by the way:

  • Arsc<P>: an intrusive & thread-safe reference-counting pointer wrapper.

Other utilities are currently under development.

Modules

  • This module deals with intrusive & thread-safe reference-counting pointers.

Macros

Structs

  • A pointer type that uniquely owns its data on the stack. but via the implementation of a mutable reference to its ManuallyDrop wrapped form.

Traits

  • Represents a smart pointer whose pointed data can be safely moved out by into_inner.
  • Smart pointers that can convert themselves into raw forms, and is able to convert raw forms back to themselves.
  • Smart pointers that can convert their uninitialized forms into initialized forms.

Functions

  • Attempts to move out the underlying pinned value, if it is Unpin.
  • Moves out the underlying pinned value, if it is Unpin.

Type Aliases

  • A wrapper that both owns and pins data on the stack.