Pinning and Async

I have been reading the rust async-book. But in the pinning section, Pinning - Asynchronous Programming in Rust, there is a paragraph that I have trouble understanding.

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 .

  1. I was under the impression that all the Futures are !Unpin (not unpin). Is there any way to tell whether an async function or block creates a Future that is Unpin or not?
  2. If the Future is !Unpin, then the above paragraph says that pinned version of it (Pin<Box> or Pin<&mut dyn Future> ) is Unpin. But when I checked the Pin in std::pin - Rust, I saw that the pinned version is Unpin only if the original Future is also Unpin.
  3. Are the pinned versions of the Future also Futures? as said here:

Pin<Box<Fut>> and Pin<&mut Fut> can both be used as futures,

Because I don't see anything related to that in the trait implementations for the Pin type.
Can someone please help me here? Thanks.

Generally a type must be !Unpin if it is self-referential, i.e. if it contains two fields, one of which contains a reference to the other. For example:

async {
    let a = "test".to_string();
    let b = a.as_str();
    
    yield_now().await;
    
    println!("{}", b);
}

This has to be !Unpin because both a and b must be fields in the generated future object as they are kept across an await, and b contains a reference to a. When Rust generates async blocks, it will sometimes make it !Unpin even if it doesn't have to, but what I described is when it has to be !Unpin.

As an example of a future that is Unpin, you could consider Tokio's Delay object, which is a future and Unpin. This is because it is not a self-referential struct.

If you take a look at the doc for Future, you will find the following impl:

impl<P> Future for Pin<P> where
    P: Unpin + DerefMut,
    <P as Deref>::Target: Future,

This is okay because a Pin<P> is some kind of pointer to a future — the future is not stored in the Pin object itself. This means that moving the Pin<P> object doesn't move the future itself. The generic trait bounds allow the pointer to be any kind of pointer with mutable access, e.g. &mut F or Box<F> but not &F, as long as the pointer points to a future.

It doesn't require the pointed-to future to be Unpin, because if a Pin<P> object exists, then either:

  1. Someone used unsafe to create the Pin<P> object, or
  2. The object inside Pin<P> can never be accessed without an unsafe block.

The case differs with the kind of pointer: For P = &mut F, we have the first case, and for P = Box<F>, we have the second. In either case, the future behind the pin can never be moved without an incorrect unsafe block somewhere.

Yep, that's the impl I mentioned before. E.g. with P = &mut F we have an

impl<F: Future> Future for Pin<&mut F>

and for P = Box<F> we have an

impl<F: Future> Future for Pin<Box<F>>
3 Likes

Thanks, Alice. Most of my doubts were due to the fact that I failed to realize that it's the pointer that is stored in Pin struct and not the pointee itself. And that the pointer can be Unpin even if the pointee (here the Future) is !Unpin.

1 Like

BTW, I was wondering are there any !Unpin types that can be stored in the Pin struct? I assume this !Unpin type should be some kind of a pointer to some other data, as that is what Pin is for...

In principle it could happen, but there are no such pointer types in practice. Note that it would not satisfy the generics:

impl<P> Future for Pin<P> where
    P: Unpin + DerefMut,
    <P as Deref>::Target: Future,

It requires that P: Unpin.

I see. Thanks

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.