[Rust-async-book]--4--Pinning[翻译
To poll futures, they must be pinned using a special type called Pin<T>
. If you read the explanation of the Future trait in the previous section "Executing Future
s and Tasks", you'll recognise Pin
from the self: Pin<&mut Self>
in the Future::poll
method's definition. But what does it mean, and why do we need it?
要进行poll futures,必须使用名为“Pin<T>”的特殊类型固定它们。如果您阅读了前一节 "Executing Future's and Tasks"中对the Future trait 的解释,在“Future::poll”方法的定义中,您将从“self:Pin<&mut self>`中识别“Pin”。但这意味着什么,为什么我们需要它?
Why Pinning
Pinning makes it possible to guarantee that an object won't ever be moved. To understand why this is necessary, we need to remember how async/.await works. Consider the following code:
固定可以保证对象不会移动。为了理解为什么这是必要的,我们需要记住async/.await是如何工作的。请考虑以下代码:
let fut_one = ...;
let fut_two = ...;
async move {
fut_one.await;
fut_two.await;
}
Under the hood, this creates an anonymous type that implements Future, providing a poll method that looks something like this:
在引擎下,这将创建一个实现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 Future for AsyncFuture {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> 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(()),
}
}
}
}
When poll is first called, it will poll fut_one. If fut_one can't complete, AsyncFuture::poll will return. Future calls to poll will pick up where the previous one left off. This process continues until the future is able to successfully complete.
当第一次调用poll时,它将对 fut_one 进行轮询。如果无法完成,AsyncFuture::poll将返回。Future 调用 poll 将从上一次的中断中恢复。这个进程一直持续到将来能够成功完成为止。
However, what happens if we have an async block that uses references? For example:
但是,如果我们有一个使用引用的 async block 异步块,会发生什么情况?例如:
async {
let mut x = [0; 128];
let read_into_buf_fut = read_into_buf(&mut x);
read_into_buf_fut.await;
println!("{:?}", x);
}
What struct does this compile down to?
这个编译到什么结构?
struct ReadIntoBuf<'a> {
buf: &'a mut [u8], // points to `x` below
}
struct AsyncFuture {
x: [u8; 128],
read_into_buf_fut: ReadIntoBuf<'what_lifetime?>,
}
Here, the ReadIntoBuf future holds a reference into the other field of our structure, x. However, if AsyncFuture is moved, the location of x will move as well, invalidating the pointer stored in read_into_buf_fut.buf.
这里,read into buf future保存对结构的另一个字段x的引用。但是,如果AsyncFuture被移动,x的位置也将移动,从而使 read_into_buf_fut.buf 中存储的指针失效。
Pinning futures to a particular spot in memory prevents this problem, making it safe to create references to values inside an async block.
将futures固定到内存中的特定位置可以防止此问题,从而可以安全地在异步块中创建对值的引用。
How to Use Pinning
The Pin type wraps pointer types, guaranteeing that the values behind the pointer won't be moved. For example, Pin<&mut T>, Pin<&T>, Pin<Box<T>> all guarantee that T won't be moved.
Pin类型包装指针类型,保证指针后面的值不会被移动。例如,Pin<&mut T>、Pin<&T>、Pin<Box<T>>都保证T不会被移动。
Most types don't have a problem being moved. These types implement a trait called Unpin. Pointers to Unpin types can be freely placed into or taken out of Pin. For example, u8 is Unpin, so Pin<&mut u8> behaves just like a normal &mut u8.
大多数类型的移动没有问题。这些类型实现了一个称为Unpin的特性。指向Unpin类型的指针可以自由地放入或取出Pin。例如,u8是Unpin,所以Pin<&mut u8>的行为与普通的&mut u8一样。
Some functions require the futures they work with to be Unpin. To use a Future or Stream that isn't Unpin with a function that requires Unpin types, you'll first have to pin the value using either Box::pin (to create a Pin<Box<T>>) or the pin_utils::pin_mut! macro (to create a Pin<&mut T>). Pin<Box<Fut>> and Pin<&mut Fut> can both be used as futures, and both implement Unpin.
有些 functions 需要与它们一起工作的 futures 才能解开。若要将没有未固定的 Future或者Stream与需要取消固定类型的函数一起使用,首先必须使用Box::pin(创建pin<Box<t>>)或pin_utils::pin_mut来固定值!宏(创建管脚)。Pin<Box<Fut>>和Pin<mut Fut>都可以用作futures,并且都可以实现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::pin(fut);
execute_unpin_future(fut); // OK
// Pinning with `pin_mut!`:
let fut = async { ... };
pin_mut!(fut);
execute_unpin_future(fut); // OK