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
中放入或取出。例如,u8
是 Unpin
,所以 Pin<&mut T>
表现得像正常一样 &mut T
。
有些函数需要与 Unpin
合作的 futures
。要使用不是 Unpin
的 Future
或 Stream
与需要 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