Today I realized a new crate called
pin-cell. This crate contains a
PinCell, which is a kind of cell that is similar to
but only can allow pinned mutable references into its interior. Right now, the
crate is nightly only and no-std compatible.
How is the API of
PinCell different from
When you call
borrow_mut on a
RefCell, you get a type back that implements
DerefMut, allowing you to mutate the interior value. This is quite powerful:
once you have a mutable reference into the
RefCell, you can not only mutate
the value inside, but move it out of it (as long as you replace it with a valid
PinCell is not quite so powerful: its
borrow_mut returns a type that does
DerefMut, and instead only allows you to get a
Pin<&mut T> to
the interior value. This pinned mutable reference guarantees that you cannot
move the interior value, only mutate it in place. This is still powerful enough
for methods like
Future::poll, but not for functions like
Here’s an example of using the
PinCell API to poll a future using interior
let cell: PinCell<impl Future>; let mut ref_mut: pin_cell::PinMut<impl Future> = cell.borrow_mut(); let pin: Pin<&mut impl Future> = cell.as_mut(); // Poll the future: pin.poll(cx);
PinCell useful? What is “pin projection”?
PinCell is useful because it is not safe to get a pinned reference into a
RefCell. This operation - getting a pinned reference into something - is
called pin projection, and is the aspect of pinning about which one must be
The term “projection” refers, in the abstract, to the operation of going from an object to an other object contained within that object - for example, going from a struct to one if its fields, or a vector to one of its elements. In Rust, one can project under different “modes” - one can project by value, or by reference, or by mutable reference, for example. And there are restrictions on when each of these projections is allowed.
“Pin projection” is projecting from a pinned reference to an object to a pinned
reference to something contained within that object. For pin projection from
one type (called
T) to another type (called
U) to be safe, there are a few
The first requirement is that
(T: Unpin) implies that
(U: Unpin). That is,
T ever implements
U does not, pin projecting from a value
T to a value of
U would be unsafe. This is because its possible that if
U is not, you could possibly move
U by moving
RefCell meets this requirement, so that’s not the problem here.
The second requirement is that the
Drop implementation of
T never moves the
U you are trying to project to. If the
Drop implementation does
move the value of
U, that is violating the guarantee of
Pin that you will
never move that value again. Other than checking that there is no
we can’t automatically check for this, which is one of the reasons pin
projection requires users to write unsafe code. However, RefCell meets this
requirement to, so that’s not the problem.
The final requirement - and the one
RefCell violates, is that you cannot
project from an
&T to an
&mut U. Pins allow you to get immutable access to
the value they are pinning, because (without interior mutability) you cannot
possibly move out of an immutable reference. The entire point of
that it gives you a mutable reference to the value it wraps, out of which you
could easily move.
And so, as a result of this, you cannot pin project through a
is why there’s value in an additional
PinCell type, which is safe to project
through (because it never gives you an unpinned mutable reference to the
interior value from an immutable reference).
Are there similar APIs that need duplication with pinning?
Yes, the thread-safe analogs of
RefCell are similarly unsafe to pin project
through, and users would need a “pin-safe” analog. The types I’m refering to
are types like
It’s also worth considering creating pin-safe versions of the lock free collections, which would necessarily be “grow only” collections - you can put things into the collection, but never take them out (because that moves the things you take out). These APIs are not in the standard library though.
Future of this crate
The crate is currently released on the 0.1.X version line. I intend to take it to 1.0.0 once the pin APIs and arbitrary self types (the nightly features it relies on) are stable. There are some known gaps in its API in comparison to RefCell, I’d be glad to accept PRs if anyone is interested in implementing them, and I’ve opened some issues with comments.