+
+To poll futures, they must be pinned using a special type called
+Pin<T>
. If you read the explanation of the Future
trait in the
+previous section "Executing Future
s and Tasks", you'll recognize
+Pin
from the self: Pin<&mut Self>
in the Future::poll
method's definition.
+But what does it mean, and why do we need it?
+
+Pin
works in tandem with the Unpin
marker. Pinning makes it possible
+to guarantee that an object implementing !Unpin
won't ever be moved. To understand
+why this is necessary, we need to remember how async
/.await
works. Consider
+the following code:
+let fut_one = /* ... */;
+let fut_two = /* ... */;
+async move {
+ fut_one.await;
+ fut_two.await;
+}
+Under the hood, this creates an anonymous type that implements Future
,
+providing a poll
method that looks something like this:
+// The `Future` type generated by our `async { ... }` block
+struct AsyncFuture {
+ fut_one: FutOne,
+ fut_two: FutTwo,
+ state: State,
+}
+
+// List of states our `async` block can be in
+enum State {
+ AwaitingFutOne,
+ AwaitingFutTwo,
+ Done,
+}
+
+impl Future for AsyncFuture {
+ type Output = ();
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
+ loop {
+ match self.state {
+ State::AwaitingFutOne => match self.fut_one.poll(..) {
+ Poll::Ready(()) => self.state = State::AwaitingFutTwo,
+ Poll::Pending => return Poll::Pending,
+ }
+ State::AwaitingFutTwo => match self.fut_two.poll(..) {
+ Poll::Ready(()) => self.state = State::Done,
+ Poll::Pending => return Poll::Pending,
+ }
+ State::Done => return Poll::Ready(()),
+ }
+ }
+ }
+}
+When poll
is first called, it will poll fut_one
. If fut_one
can't
+complete, AsyncFuture::poll
will return. Future calls to poll
will pick
+up where the previous one left off. This process continues until the future
+is able to successfully complete.
+However, what happens if we have an async
block that uses references?
+For example:
+async {
+ let mut x = [0; 128];
+ let read_into_buf_fut = read_into_buf(&mut x);
+ read_into_buf_fut.await;
+ println!("{:?}", x);
+}
+What struct does this compile down to?
+struct ReadIntoBuf<'a> {
+ buf: &'a mut [u8], // points to `x` below
+}
+
+struct AsyncFuture {
+ x: [u8; 128],
+ read_into_buf_fut: ReadIntoBuf<'what_lifetime?>,
+}
+Here, the ReadIntoBuf
future holds a reference into the other field of our
+structure, x
. However, if AsyncFuture
is moved, the location of x
will
+move as well, invalidating the pointer stored in read_into_buf_fut.buf
.
+Pinning futures to a particular spot in memory prevents this problem, making
+it safe to create references to values inside an async
block.
+
+Let's try to understand pinning by using an slightly simpler example. The problem we encounter
+above is a problem that ultimately boils down to how we handle references in self-referential
+types in Rust.
+For now our example will look like this:
+#[derive(Debug)]
+struct Test {
+ a: String,
+ b: *const String,
+}
+
+impl Test {
+ fn new(txt: &str) -> Self {
+ Test {
+ a: String::from(txt),
+ b: std::ptr::null(),
+ }
+ }
+
+ fn init(&mut self) {
+ let self_ref: *const String = &self.a;
+ self.b = self_ref;
+ }
+
+ fn a(&self) -> &str {
+ &self.a
+ }
+
+ fn b(&self) -> &String {
+ assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
+ unsafe { &*(self.b) }
+ }
+}
+Test
provides methods to get a reference to the value of the fields a
and b
. Since b
is a
+reference to a
we store it as a pointer since the borrowing rules of Rust don't allow us to
+define this lifetime. We now have what we call a self-referential struct.
+Our example works fine if we don't move any of our data around as you can observe by running
+this example:
+fn main() {
+ let mut test1 = Test::new("test1");
+ test1.init();
+ let mut test2 = Test::new("test2");
+ test2.init();
+
+ println!("a: {}, b: {}", test1.a(), test1.b());
+ println!("a: {}, b: {}", test2.a(), test2.b());
+
+}
+#[derive(Debug)]
+struct Test {
+ a: String,
+ b: *const String,
+}
+
+impl Test {
+ fn new(txt: &str) -> Self {
+ Test {
+ a: String::from(txt),
+ b: std::ptr::null(),
+ }
+ }
+
+ // We need an `init` method to actually set our self-reference
+ fn init(&mut self) {
+ let self_ref: *const String = &self.a;
+ self.b = self_ref;
+ }
+
+ fn a(&self) -> &str {
+ &self.a
+ }
+
+ fn b(&self) -> &String {
+ assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
+ unsafe { &*(self.b) }
+ }
+}
+We get what we'd expect:
+a: test1, b: test1
+a: test2, b: test2
+Let's see what happens if we swap test1
with test2
and thereby move the data:
+fn main() {
+ let mut test1 = Test::new("test1");
+ test1.init();
+ let mut test2 = Test::new("test2");
+ test2.init();
+
+ println!("a: {}, b: {}", test1.a(), test1.b());
+ std::mem::swap(&mut test1, &mut test2);
+ println!("a: {}, b: {}", test2.a(), test2.b());
+
+}
+#[derive(Debug)]
+struct Test {
+ a: String,
+ b: *const String,
+}
+
+impl Test {
+ fn new(txt: &str) -> Self {
+ Test {
+ a: String::from(txt),
+ b: std::ptr::null(),
+ }
+ }
+
+ fn init(&mut self) {
+ let self_ref: *const String = &self.a;
+ self.b = self_ref;
+ }
+
+ fn a(&self) -> &str {
+ &self.a
+ }
+
+ fn b(&self) -> &String {
+ assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
+ unsafe { &*(self.b) }
+ }
+}
+Naively, we could think that what we should get a debug print of test1
two times like this:
+a: test1, b: test1
+a: test1, b: test1
+But instead we get:
+a: test1, b: test1
+a: test1, b: test2
+The pointer to test2.b
still points to the old location which is inside test1
+now. The struct is not self-referential anymore, it holds a pointer to a field
+in a different object. That means we can't rely on the lifetime of test2.b
to
+be tied to the lifetime of test2
anymore.
+If you're still not convinced, this should at least convince you:
+fn main() {
+ let mut test1 = Test::new("test1");
+ test1.init();
+ let mut test2 = Test::new("test2");
+ test2.init();
+
+ println!("a: {}, b: {}", test1.a(), test1.b());
+ std::mem::swap(&mut test1, &mut test2);
+ test1.a = "I've totally changed now!".to_string();
+ println!("a: {}, b: {}", test2.a(), test2.b());
+
+}
+#[derive(Debug)]
+struct Test {
+ a: String,
+ b: *const String,
+}
+
+impl Test {
+ fn new(txt: &str) -> Self {
+ Test {
+ a: String::from(txt),
+ b: std::ptr::null(),
+ }
+ }
+
+ fn init(&mut self) {
+ let self_ref: *const String = &self.a;
+ self.b = self_ref;
+ }
+
+ fn a(&self) -> &str {
+ &self.a
+ }
+
+ fn b(&self) -> &String {
+ assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
+ unsafe { &*(self.b) }
+ }
+}
+The diagram below can help visualize what's going on:
+Fig 1: Before and after swap
+
+It's easy to get this to show undefined behavior and fail in other spectacular ways as well.
+
+Let's see how pinning and the Pin
type can help us solve this problem.
+The Pin
type wraps pointer types, guaranteeing that the values behind the
+pointer won't be moved if it is not implementing Unpin
. For example, Pin<&mut T>
, Pin<&T>
, Pin<Box<T>>
all guarantee that T
won't be moved if T: !Unpin
.
+Most types don't have a problem being moved. These types implement a trait
+called Unpin
. Pointers to Unpin
types can be freely placed into or taken
+out of Pin
. For example, u8
is Unpin
, so Pin<&mut u8>
behaves just like
+a normal &mut u8
.
+However, types that can't be moved after they're pinned have a marker called
+!Unpin
. Futures created by async/await are an example of this.
+
+Back to our example. We can solve our problem by using Pin
. Let's take a look at what
+our example would look like if we required a pinned pointer instead:
+use std::pin::Pin;
+use std::marker::PhantomPinned;
+
+#[derive(Debug)]
+struct Test {
+ a: String,
+ b: *const String,
+ _marker: PhantomPinned,
+}
+
+
+impl Test {
+ fn new(txt: &str) -> Self {
+ Test {
+ a: String::from(txt),
+ b: std::ptr::null(),
+ _marker: PhantomPinned, // This makes our type `!Unpin`
+ }
+ }
+
+ fn init(self: Pin<&mut Self>) {
+ let self_ptr: *const String = &self.a;
+ let this = unsafe { self.get_unchecked_mut() };
+ this.b = self_ptr;
+ }
+
+ fn a(self: Pin<&Self>) -> &str {
+ &self.get_ref().a
+ }
+
+ fn b(self: Pin<&Self>) -> &String {
+ assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
+ unsafe { &*(self.b) }
+ }
+}
+Pinning an object to the stack will always be unsafe
if our type implements
+!Unpin
. You can use a crate like pin_utils
to avoid writing
+our own unsafe
code when pinning to the stack.
+Below, we pin the objects test1
and test2
to the stack:
+pub fn main() {
+ // test1 is safe to move before we initialize it
+ let mut test1 = Test::new("test1");
+ // Notice how we shadow `test1` to prevent it from being accessed again
+ let mut test1 = unsafe { Pin::new_unchecked(&mut test1) };
+ Test::init(test1.as_mut());
+
+ let mut test2 = Test::new("test2");
+ let mut test2 = unsafe { Pin::new_unchecked(&mut test2) };
+ Test::init(test2.as_mut());
+
+ println!("a: {}, b: {}", Test::a(test1.as_ref()), Test::b(test1.as_ref()));
+ println!("a: {}, b: {}", Test::a(test2.as_ref()), Test::b(test2.as_ref()));
+}
+use std::pin::Pin;
+use std::marker::PhantomPinned;
+
+#[derive(Debug)]
+struct Test {
+ a: String,
+ b: *const String,
+ _marker: PhantomPinned,
+}
+
+
+impl Test {
+ fn new(txt: &str) -> Self {
+ Test {
+ a: String::from(txt),
+ b: std::ptr::null(),
+ // This makes our type `!Unpin`
+ _marker: PhantomPinned,
+ }
+ }
+
+ fn init(self: Pin<&mut Self>) {
+ let self_ptr: *const String = &self.a;
+ let this = unsafe { self.get_unchecked_mut() };
+ this.b = self_ptr;
+ }
+
+ fn a(self: Pin<&Self>) -> &str {
+ &self.get_ref().a
+ }
+
+ fn b(self: Pin<&Self>) -> &String {
+ assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
+ unsafe { &*(self.b) }
+ }
+}
+Now, if we try to move our data now we get a compilation error:
+pub fn main() {
+ let mut test1 = Test::new("test1");
+ let mut test1 = unsafe { Pin::new_unchecked(&mut test1) };
+ Test::init(test1.as_mut());
+
+ let mut test2 = Test::new("test2");
+ let mut test2 = unsafe { Pin::new_unchecked(&mut test2) };
+ Test::init(test2.as_mut());
+
+ println!("a: {}, b: {}", Test::a(test1.as_ref()), Test::b(test1.as_ref()));
+ std::mem::swap(test1.get_mut(), test2.get_mut());
+ println!("a: {}, b: {}", Test::a(test2.as_ref()), Test::b(test2.as_ref()));
+}
+use std::pin::Pin;
+use std::marker::PhantomPinned;
+
+#[derive(Debug)]
+struct Test {
+ a: String,
+ b: *const String,
+ _marker: PhantomPinned,
+}
+
+
+impl Test {
+ fn new(txt: &str) -> Self {
+ Test {
+ a: String::from(txt),
+ b: std::ptr::null(),
+ _marker: PhantomPinned, // This makes our type `!Unpin`
+ }
+ }
+
+ fn init(self: Pin<&mut Self>) {
+ let self_ptr: *const String = &self.a;
+ let this = unsafe { self.get_unchecked_mut() };
+ this.b = self_ptr;
+ }
+
+ fn a(self: Pin<&Self>) -> &str {
+ &self.get_ref().a
+ }
+
+ fn b(self: Pin<&Self>) -> &String {
+ assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
+ unsafe { &*(self.b) }
+ }
+}
+The type system prevents us from moving the data, as follows:
+error[E0277]: `PhantomPinned` cannot be unpinned
+ --> src\test.rs:56:30
+ |
+56 | std::mem::swap(test1.get_mut(), test2.get_mut());
+ | ^^^^^^^ within `test1::Test`, the trait `Unpin` is not implemented for `PhantomPinned`
+ |
+ = note: consider using `Box::pin`
+note: required because it appears within the type `test1::Test`
+ --> src\test.rs:7:8
+ |
+7 | struct Test {
+ | ^^^^
+note: required by a bound in `std::pin::Pin::<&'a mut T>::get_mut`
+ --> <...>rustlib/src/rust\library\core\src\pin.rs:748:12
+ |
+748 | T: Unpin,
+ | ^^^^^ required by this bound in `std::pin::Pin::<&'a mut T>::get_mut`
+
+
+It's important to note that stack pinning will always rely on guarantees
+you give when writing unsafe
. While we know that the pointee of &'a mut T
+is pinned for the lifetime of 'a
we can't know if the data &'a mut T
+points to isn't moved after 'a
ends. If it does it will violate the Pin
+contract.
+A mistake that is easy to make is forgetting to shadow the original variable
+since you could drop the Pin
and move the data after &'a mut T
+like shown below (which violates the Pin contract):
+fn main() {
+ let mut test1 = Test::new("test1");
+ let mut test1_pin = unsafe { Pin::new_unchecked(&mut test1) };
+ Test::init(test1_pin.as_mut());
+
+ drop(test1_pin);
+ println!(r#"test1.b points to "test1": {:?}..."#, test1.b);
+
+ let mut test2 = Test::new("test2");
+ mem::swap(&mut test1, &mut test2);
+ println!("... and now it points nowhere: {:?}", test1.b);
+}
+use std::pin::Pin;
+use std::marker::PhantomPinned;
+use std::mem;
+
+#[derive(Debug)]
+struct Test {
+ a: String,
+ b: *const String,
+ _marker: PhantomPinned,
+}
+
+
+impl Test {
+ fn new(txt: &str) -> Self {
+ Test {
+ a: String::from(txt),
+ b: std::ptr::null(),
+ // This makes our type `!Unpin`
+ _marker: PhantomPinned,
+ }
+ }
+
+ fn init<'a>(self: Pin<&'a mut Self>) {
+ let self_ptr: *const String = &self.a;
+ let this = unsafe { self.get_unchecked_mut() };
+ this.b = self_ptr;
+ }
+
+ #[allow(unused)]
+ fn a<'a>(self: Pin<&'a Self>) -> &'a str {
+ &self.get_ref().a
+ }
+
+ #[allow(unused)]
+ fn b<'a>(self: Pin<&'a Self>) -> &'a String {
+ assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
+ unsafe { &*(self.b) }
+ }
+}
+
+
+Pinning an !Unpin
type to the heap gives our data a stable address so we know
+that the data we point to can't move after it's pinned. In contrast to stack
+pinning, we know that the data will be pinned for the lifetime of the object.
+use std::pin::Pin;
+use std::marker::PhantomPinned;
+
+#[derive(Debug)]
+struct Test {
+ a: String,
+ b: *const String,
+ _marker: PhantomPinned,
+}
+
+impl Test {
+ fn new(txt: &str) -> Pin<Box<Self>> {
+ let t = Test {
+ a: String::from(txt),
+ b: std::ptr::null(),
+ _marker: PhantomPinned,
+ };
+ let mut boxed = Box::pin(t);
+ let self_ptr: *const String = &boxed.a;
+ unsafe { boxed.as_mut().get_unchecked_mut().b = self_ptr };
+
+ boxed
+ }
+
+ fn a(self: Pin<&Self>) -> &str {
+ &self.get_ref().a
+ }
+
+ fn b(self: Pin<&Self>) -> &String {
+ unsafe { &*(self.b) }
+ }
+}
+
+pub fn main() {
+ let test1 = Test::new("test1");
+ let test2 = Test::new("test2");
+
+ println!("a: {}, b: {}",test1.as_ref().a(), test1.as_ref().b());
+ println!("a: {}, b: {}",test2.as_ref().a(), test2.as_ref().b());
+}
+Some functions require the futures they work with to be Unpin
. To use a
+Future
or Stream
that isn't Unpin
with a function that requires
+Unpin
types, you'll first have to pin the value using either
+Box::pin
(to create a Pin<Box<T>>
) or the pin_utils::pin_mut!
macro
+(to create a Pin<&mut T>
). Pin<Box<Fut>>
and Pin<&mut Fut>
can both be
+used as futures, and both implement Unpin
.
+For example:
+use pin_utils::pin_mut; // `pin_utils` is a handy crate available on crates.io
+
+// A function which takes a `Future` that implements `Unpin`.
+fn execute_unpin_future(x: impl Future<Output = ()> + Unpin) { /* ... */ }
+
+let fut = async { /* ... */ };
+execute_unpin_future(fut); // Error: `fut` does not implement `Unpin` trait
+
+// Pinning with `Box`:
+let fut = async { /* ... */ };
+let fut = Box::pin(fut);
+execute_unpin_future(fut); // OK
+
+// Pinning with `pin_mut!`:
+let fut = async { /* ... */ };
+pin_mut!(fut);
+execute_unpin_future(fut); // OK
+
+
+-
+
If T: Unpin
(which is the default), then Pin<'a, T>
is entirely
+equivalent to &'a mut T
. In other words: Unpin
means it's OK for this type
+to be moved even when pinned, so Pin
will have no effect on such a type.
+
+-
+
Getting a &mut T
to a pinned T requires unsafe if T: !Unpin
.
+
+-
+
Most standard library types implement Unpin
. The same goes for most
+"normal" types you encounter in Rust. A Future
generated by async/await is an exception to this rule.
+
+-
+
You can add a !Unpin
bound on a type on nightly with a feature flag, or
+by adding std::marker::PhantomPinned
to your type on stable.
+
+-
+
You can either pin data to the stack or to the heap.
+
+-
+
Pinning a !Unpin
object to the stack requires unsafe
+
+-
+
Pinning a !Unpin
object to the heap does not require unsafe
. There is a shortcut for doing this using Box::pin
.
+
+-
+
For pinned data where T: !Unpin
you have to maintain the invariant that its memory will not
+get invalidated or repurposed from the moment it gets pinned until when drop is called. This is
+an important part of the pin contract.
+
+
+
+