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