Pin
- Rust course
- Advanced
- Lesson 5/81
- 4 min
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 stackMost 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
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
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
let mut kutulu = Box::____(async { 42 });
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 |