为了对Future调用poll,需要使用到Pin的特殊类型。本节就介绍一下Pin类型。
我们主要考虑async_main()函数中的async块(async函数也是一样,通过async都是转化为Future),实际背后会做如下工作: (1)为async块生成一个类似于如下的结构体:
struct AsyncFuture { fut_one: FutFunction1, fut_two: FutFunction2, state: State, } //state的定义可能如下 enum State { AwaitingFutFunction1, AwaitingFutFunction2, Done, }(2)为其生成对应的poll函数:
impl Future for AsyncFuture { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { loop { match self.state { State::AwaitingFutFunction1 => match self.fut_one.poll(..) { Poll::Ready(()) => self.state = State::AwaitingFutFunction2, Poll::Pending => return Poll::Pending, } State::AwaitingFutFunction2 => match self.fut_two.poll(..) { Poll::Ready(()) => self.state = State::Done, Poll::Pending => return Poll::Pending, } State::Done => return Poll::Ready(()), } } } }好,到这里,我们只是模拟编译器给async代码块进行了展开,那么和我们要讲的Pin有什么关系呢?
我们再考虑如下例子:
async fn async_put_data_to_buf(mut buf: &[u8]) { //to do ... } async fn async_main () { //重点关注这里--------- let f = async { let mut x = [0; 128]; let read_into_buf_fut = async_put_data_to_buf(&mut x); read_into_buf_fut.await; }; //--------------------- f.await; }对于上面async_main中的async块,编译器为其生成的结构如下:
struct ReadIntoBuf<'a> { buf: &'a mut [u8], // points to `x` below } struct AsyncFuture { x: [u8; 128], read_into_buf_fut: ReadIntoBuf<'what_lifetime?>, }在AsyncFuture中,read_into_buf_fut.buf指向x,相当于是一个自引用(一个字段引用另一个字段)。但是如果AsyncFuture发生移动,x肯定也会发生移动,如果read_into_buf_fut.buf还是指向原来的值的话,则会变成无效。 而Pin就是为了解决此问题的。
Pin类型包着指针类型,保证指针背后的值将不被移动。例如 Pin<&mut T>,Pin<&T>, Pin<Box> 都保证 T 不会移动。
拿上面的例子来说,如果使用Pin<>就是将x对应的那块内存固定,这样即使AsyncFuture发生移动,但是x不会移动,那么read_into_buf_fut.buf不会变成悬垂引用。
大多数类型都没有移动的问题,这些类型实现了 Unpin trait。Unpin 类型指针可以自由从 Pin 中放入或取出。例如,u8 就是 Unpin的。
如果需要Future和Unpin一起使用,则需要使用Pin<Box>或者pin_utils::pin_mut!,例子如下:
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