.await
generates code that invokes Future::poll
. The first parameter has type Pin<&mut Self>
. The main characteristic of Pin<P>
is that it represents a pointer to a value that is guaranteed to never move after the Pin<P>
is created, unless the pointee implements Unpin
(which means it doesn't care if it's moved).
Let's first answer: Why is it not necessary to pin a future in order to await it by value? That is, why does this work:
pub async fn foo() {
let fut = async {};
fut.await;
}
It's simple: fut.await
consumes fut
.
pub async fn foo() {
let fut = async {};
fut.await;
drop(fut); // error[E0382]: use of moved value: `fut`
}
There is no opportunity for us to move fut
after fut.await
, so there is no way we could violate Pin
's rules on fut
.
However, if we could .await
a reference, then we would be able to move the original value after the .await
, and that would be disastrous if fut
was self-referential.
The error comes from the compiler failing to find an implementation of Future
for the expression &mut fut
. There is impl<'_, F> Future for &'_ mut F where F: Future + Unpin + ?Sized
in the standard library; however, fut
doesn't implement Unpin
, so that's what the compiler reports in its error message.
tokio::pin!(fut)
first moves fut
(this is how the macro can ensure it has ownership of the future), then declares a new variable fut
of type Pin<&mut F>
(where F
is the original fut
's type).
Thus, the code following tokio::pin!(fut)
that uses fut
manipulates a pinned reference to the future, rather than the future value directly. If we try to move fut
, then we just move the pinned reference. And if we take a mutable reference to fut
, then we end up with a reference to a pinned reference (&mut Pin<&mut F>
). This type does implement Future
because:
&mut F
implements Unpin
even if F
doesn't implement Unpin
Pin<&mut F>
implements Unpin
because &mut F
implements Unpin
Pin<&mut F>
implements Future
because F
implements Future
&mut Pin<&mut F>
implements Future
because Pin<&mut F>
implements both Future
and Unpin