CodeBlanks Logo Icon CodeBlanks

Pin

Learn Pin<T> and why self-referential data needs it.

Pin

The state machines of async functions can hold references into themselves (for example a reference taken before an .await). If such a value is moved in memory, the inner reference points at the old address. Pin<P> guarantees that the value it points to won't move any more; that's why poll takes self: Pin<&mut Self>.

let f = async { 42 };
let mut kutulu = Box::pin(f);           // pinned on the heap
let mut yerel = std::pin::pin!(async { 1 });   // pinned on the stack

Most types (i32, String, Vec) are Unpin: moving them is safe and Pin imposes no restriction on them.

Boxed futures

Pin<Box<dyn Future>> is itself Unpin and implements Future, so it can be passed straight to block_on. Recursive async functions and returning futures from traits also use this type:

type KutuluFuture = Pin<Box<dyn Future<Output = i32>>>;

let isler: Vec<KutuluFuture> = vec![
    Box::pin(async { 1 }),
    Box::pin(async { 2 }),
];
let toplam: i32 = isler.into_iter().map(block_on).sum();

Practice List of boxed jobs

Define type KutuluFuture = Pin<Box<dyn Future<Output = i32>>>;. Put the blocks async { 1 } and async { 2 } into a Vec<KutuluFuture> with Box::pin, run each with block_on, sum and print.

Starter code

main.rs
use std::future::Future;
use std::pin::{pin, Pin};
use std::task::{Context, Poll, Waker};

fn block_on<F: Future>(f: F) -> F::Output {
    let mut f = pin!(f);
    let mut cx = Context::from_waker(Waker::noop());
    loop {
        if let Poll::Ready(v) = f.as_mut().poll(&mut cx) {
            return v;
        }
    }
}

fn main() {
    // buraya yaz
}

Sample solution

main.rs
use std::future::Future;
use std::pin::{pin, Pin};
use std::task::{Context, Poll, Waker};

fn block_on<F: Future>(f: F) -> F::Output {
    let mut f = pin!(f);
    let mut cx = Context::from_waker(Waker::noop());
    loop {
        if let Poll::Ready(v) = f.as_mut().poll(&mut cx) {
            return v;
        }
    }
}

type KutuluFuture = Pin<Box<dyn Future<Output = i32>>>;

fn main() {
    let isler: Vec<KutuluFuture> = vec![Box::pin(async { 1 }), Box::pin(async { 2 })];
    let toplam: i32 = isler.into_iter().map(block_on).sum();
    println!("{}", toplam);
}

Expected output

3

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Which type is used to keep a list made of different async blocks in a single <code>Vec</code>?

  • &dyn Future
  • Pin<Box<dyn Future<Output = T>>> Correct answer
  • Vec<impl Future>
  • Box<Future>

Explanation: Every async block is a separate type; it's brought to a common type with a trait object and pinned with Box::pin.

2 Pin on the heap

Question
let mut kutulu = Box::____(async { 42 });
Answer
let mut kutulu = Box::pin(async { 42 });

Explanation: Box::pin puts the value on the heap and pins it.

3 Concept and meaning

Term Meaning
Pin<P> Guarantee of not moving
Unpin Safe to move
Box::pin Pins on the heap
pin! Pins on the stack

All lessons in Advanced