4.1. 固定

未匹配的标注

固定

为了对futures进行轮询,必须使用名为 Pin<T>的特殊类型固定它们。上一节"执行FutureS和任务",你会认识 Pin来自Future:poll方法的定义self: Pin<&mut Self>。但它意味着什么,为什么我们需要它呢?

Why Pinning

固定可以保证对象不会移动。要理解为什么这是必要的,我们需要记住async/ await! 如何工作。考虑以下代码:

async {
    await!(fut_one);
    await!(fut_two);
}

在内部,这会创建一个实现Future的匿名类型,提供如下所示的poll方法:

// The `Future` type generated by our `async { ... }` block
struct AsyncFuture {
    fut_one: FutOne,
    fut_two: FutTwo,
    state: State,
}
 // List of states our `async` block can be in
enum State {
    AwaitingFutOne,
    AwaitingFutTwo,
    Done,
}
 impl AsyncFuture {
    fn poll(...) -> Poll<()> {
        loop {
            match self.state {
                State::AwaitingFutOne => match self.fut_one.poll(..) {
                    Poll::Ready(()) => self.state = State::AwaitingFutTwo,
                    Poll::Pending => return Poll::Pending,
                }
                State::AwaitingFutTwo => match self.fut_two.poll(..) {
                    Poll::Ready(()) => self.state = State::Done,
                    Poll::Pending => return Poll::Pending,
                }
                State::Done => return Poll::Ready(()),
            }
        }
    }
}

poll第一次调用时,它会轮询fut_one。如果fut_one无法完成,AsyncFuture::poll将返回。将来调用poll会在前一个调用停止的地方继续。这个过程一直持续到 Future 能够成功完成。

但是,如果我们有一个async块使用引用会发生什么?例如:

async {
    let mut x = [0; 128];
    let read_into_buf_fut = read_into_buf(&mut x);
    await!(read_into_buf_fut);
    println!("{:?}", x);
}

这个结构编译后是什么?

struct ReadIntoBuf<'a> {
    buf: &'a mut [u8], // points to `x` below
}
 struct AsyncFuture {
    x: [u8; 128],
    read_into_buf_fut: ReadIntoBuf<'what_lifetime?>,
}

在这里,ReadIntoBuf Future持有我们另一个结构的字段x的引用。但是,如果AsyncFuture移动,则x的位置也会移动,使read_into_buf_fut.buf存储的指针无效。

Future固定到内存中的特定位置可以防止出现此问题,从而可以安全地创建对async块内值的引用。

如何使用固定

Pin类型包着指针类型,保证指针背后的值将不被移动。例如Pin<&mut T>,Pin<&T>, Pin<Box<T>>都保证T不会移动。

大多数类型都没有移动的问题。这些类型实现了一个叫做Unpin的特征。Unpin类型指针可以自由从Pin中放入或取出。例如,u8Unpin,所以Pin<&mut T>表现得像正常一样&mut T

有些函数需要与Unpin合作的futures。要使用不是UnpinFutureStream与需要Unpin类型的函数 ,您首先必须使用Box::pinned(创建一个Pin<Box<T>>)或pin_utils::pin_mut!宏(创建一个Pin<&mut T>)来固定值 。Pin<Box<Fut>>并且Pin<&mut Fut>都可以用作Future,并且都实现了Unpin

For example:

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::pinned(fut);
execute_unpin_future(fut); // OK
 // Pinning with `pin_mut!`:
let fut = async { ... };
pin_mut!(fut);
execute_unpin_future(fut); // OK

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~