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
发起讨论 查看所有版本


暂无话题~