How compiler ensure the pinned data not to move?
It doesn't, Pin
uses unsafe
and a very clever API to guarantee that nothing is moved. Pin
is a library construct with no special connection to the compiler.
Specifically Pin
is used as Pin<Pointer<T>>
(for some pointer type), and it's the T
that is pinned, not the pointer.
Pin
functions by preventing (safe) access to Pointer<T>
(at all) or &mut T
, only allowing &T
(shared, basically read-only) access. You need to use unsafe
code to get at &mut T
, and promise on pain of UB not to move the T
behind the reference.
Also, to add on this, since it is a common misconception of Pin
(which I'd qualify as a nice abstraction when used with futures / async
/ generators, but a very subtle and thus easy to misuse and thus dangerous abstraction elsewhere, that should thus then be avoided): given a Pin<Pointer<T>
(more precisely, a Pin<impl Deref<Target = T>>
, and when T
is not Unpin
(since Unpin
is a marker trait to opt-out of all of Pin
-related API restrictions), then one cannot, without unsafe
, get a &mut T
. Nothing more! This is an abstraction that can be relied on by unsafe
code when:
-
Pointer<T>
does indeed involve a level of pointer indirection, so that moving aPointer<T>
does not move itsT
.-
Counter-example:
Pin<Wrapper<T>>
, whereWrapper
is defined as:struct Wrapper<T>(T); impl<T> ::core::ops::Deref for Wrapper<T> { type Target = T; fn deref (self: &'_ Wrapper<T>) -> &'_ T { &self.0 } }
More generally, it is fine when
Pointer<T>
features a "stable deref", so that no matter how many times it is moved, its&'_ <Pointer<T>>::Target
always points to the same place.In practice, the most usual such
Pointer
s aretype Pointer<T> = Box<T>
andtype Pointer<T> = &mut T;
, which are the ones involved inFuture
-related APIs. -
-
T
is notUnpin
. Classic counter-example:Pin<Box<str>>
or things like that: in those casesPin
is as useful as nipples on a breastplate. -
the
unsafe
code that relies on these guarantees interacts withT
directly.-
Counter-example:
T = Cell<Inner>
for some (non-Unpin
)Inner
type.Indeed, the natural non-
Mut
Deref
that transparently ignoresPin
allows one to get&Cell<Inner>
, which means that one can then useCell::swap
to move theInner
value.More generally, in order to be able to say that if
type T = SomeWrapper<Inner>
, then by "pinning"T
you'd like to also have the property of having "pinned" theInner
value, you need forSomeWrapper
to provide a "structural pinning projection". This has historically been one of the most subtle parts of this design, but nowadays the crate pin_project - Rust allows to write these properties in a compile-time checked fashion (i.e., the common mistakes when doing so are caught at compile-time by the crate-provided attribute( macro)s).
-
The Pin
itself could be moved, right?
Yes, the Pin
can be moved, but this is not a problem because a Pin
should always wrap a reference or pointer, and moving a reference/pointer does not move the thing it points at.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.