Expand description
The stackpin crate exposes a StackPinned type that allows to represent !Unpin data that should be pinned to the stack
at the point of declaration.
The crate exposes a trait, FromUnpinned, as well as a stack_let macro that makes safely creating StackPinned instances easier.
The crate also exposes the PinStack type alias for Pin<StackPinned<T>>.
This crate was inspired from the pin-utils crate, the main differences being:
- pin-utils provides a macro to return a
Pin<&mut T>instance, with a “mutable reference” semantics that includes reborrow. Thestackpincrate promotes a “root handle” semantics that guarantees that a function consuming aPinStack<T>consumes the only handle toT, and not a reborrowed reference. - The syntax for the
stack_let!(mut id : ty = expr)macro attempts to mimic a regularlet mut id : ty = exprstatement. - The provided
FromUnpinnedtrait andUnpinnedstruct aim at separating unmovable types from the data that can be used to construct them.stackpinaims at promoting a model where all unmovable types are only accessible once pinned. - The
StackPinned<T>type expresses strong guarantee about the fact that the destructor forTwill be run. - The
stackpincrate solely focuses on stack pinning. The pin-utils crate also provides other utilities such as pin projection.
§Stack pinnable types
A type T that wants to benefit from the guarantees provided by StackPinned should be
!Unpin. This is necessary to enforce the “drop will be run” guarantee.
Additionally, the stackpin crate promotes an idiom where “unmovable” types are strictly
separated from movable types, and are preferably only accessible through PinStack.
For example, let’s consider the following Unmovable struct (from the documentation for the
pin module):
use std::marker::PhantomPinned;
use std::ptr::NonNull;
struct Unmovable {
// Owned data
s: String,
// Self referential pointer meant to point to `s`
slice: NonNull<String>,
// Obligatory marker that makes this struct `!Unpin`.
// Without this, implementing `FromUnpinned` for `Unmovable` would not be safe.
_pinned: PhantomPinned,
}It is important to note that this struct is not unmovable by itself, as there are no such types in Rust.
Instead, we are going to enforce this through privacy: since the fields of the struct are private, no instance can be created
from outside the module.
Similarly, no public “constructor” function pub fn new() -> Unmovable should be provided.
So, how will clients consume Unmovable instances?
The recommended solution using stackpin is to implement FromUnpinned<Data> for Unmovable, where Data is the
type that would normally serve as parameters in a “constructor” function.
use stackpin::FromUnpinned;
// An `Unmovable` can be created from a `String`
unsafe impl FromUnpinned<String> for Unmovable {
// This associated type can be used to retain information between the creation of the instance and its pinning.
// This allows for some sort of "two-steps initialization" without having to store the initialization part in the
// type itself.
// Here, we don't need it, so we just set it to `()`.
type PinData = ();
// Simply builds the Unmovable from the String.
// The implementation of this function is not allowed to consider that the type won't ever move **yet**.
// (in particular, the `Self` instance is returned by this function)
// Note, however, that safe users of FromUnpinned will:
// * Not do anything to with the returned `Self` instance between the call to
// `from_unpinned` and the call to `on_pin`.
// * Not panic between calling the two functions
// * Always call the second function if the first has been called.
unsafe fn from_unpinned(s: String) -> (Self, ()) {
(
Self {
s,
// We will "fix" this dangling pointer once the data will be pinned
// and guaranteed not to move anymore.
slice: NonNull::dangling(),
_pinned: PhantomPinned,
},
(),
)
}
// Performs a second initialization step on an instance that is already guaranteed to never move again.
// This allows to e.g. set self borrow with the guarantee that they will remain valid.
unsafe fn on_pin(&mut self, _data: ()) {
// Data will never move again, set the pointer to our own internal String whose address
// will never change anymore
self.slice = NonNull::from(&self.s);
}
}With FromUnpinned<Data> implemented for T, one can now add a “constructor method” that would return an
Unpinned<Data, T>. The Unpinned<U, T> struct is a simple helper struct around U that maintains the destination
type T. This is used by the stack_let macro to infer the type of T that the user may want to produce.
impl Unmovable {
fn new_unpinned<T: Into<String>>(s: T) -> Unpinned<String, Unmovable> {
Unpinned::new(s.into())
}
}Then, a user of the Unmovable struct can simply build an instance by using the stack_let macro:
use stackpin::stack_let;
// ...
stack_let!(unmovable = Unmovable::new_unpinned("Intel the Beagle")); // this creates the unmovable instance on the stack and binds `unmovable` with a `PinStack<Unmovable>`
// ...Macros§
- stack_
let stack_let!(id = expr)binds aPinStack<T>toidifexpris an expression of typeUwhereT: FromUnpinned<U>.
Structs§
- Stack
Pinned - Struct that represents data that is pinned to the stack, at the point of declaration.
- Unpinned
- A helper struct around
Uthat remembers theTdestination type.
Traits§
- From
Unpinned - Trait to build
StackPinnedvalues from unpinned types.
Type Aliases§
- PinStack
- Short-hand for
Pin<StackPinned<T>>