为什么我需要先固定一个 future,然后才能等待对它的引用?

Enn*_*ael 6 future rust async-await

tokio教程的select!状态:

需要注意的是,要等待引用,被引用的值必须被固定或实现 Unpin。

事实上,以下代码无法编译:

let fut = example(); // example is an async fn
(&mut fut).await;
Run Code Online (Sandbox Code Playgroud)

并出现以下错误消息:

error[E0277]: `from_generator::GenFuture<[static generator@src/main.rs:15:27: 17:2]>` cannot be unpinned
... snip ...
within `impl futures::Future<Output = i32>`, the trait `Unpin` is not implemented for `from_generator::GenFuture<[static generator@src/main.rs:15:27: 17:2]>
... snip ...
note: consider using `Box::pin`
Run Code Online (Sandbox Code Playgroud)

固定未来可以解决这个问题:

error[E0277]: `from_generator::GenFuture<[static generator@src/main.rs:15:27: 17:2]>` cannot be unpinned
... snip ...
within `impl futures::Future<Output = i32>`, the trait `Unpin` is not implemented for `from_generator::GenFuture<[static generator@src/main.rs:15:27: 17:2]>
... snip ...
note: consider using `Box::pin`
Run Code Online (Sandbox Code Playgroud)

为什么需要固定未来以等待对它的引用?

Fra*_*gné 8

.await生成调用Future::poll. 第一个参数的类型为Pin<&mut Self>。的主要特征Pin<P>是它代表一个指向值的指针,该值在Pin<P>创建后保证永远不会移动,除非被指向者实现Unpin(这意味着它不关心它是否被移动)。

让我们首先回答:为什么没有必要固定一个 future 来按值等待它?也就是说,为什么这会起作用:

pub async fn foo() {
    let fut = async {};
    fut.await;
}
Run Code Online (Sandbox Code Playgroud)

很简单:fut.await消耗fut。

pub async fn foo() {
    let fut = async {};
    fut.await;
    drop(fut); // error[E0382]: use of moved value: `fut`
}
Run Code Online (Sandbox Code Playgroud)

我们没有机会继续前进fut,fut.await所以我们不可能违反Pin上面的规则fut。

但是,如果我们可以引用.await,那么我们就可以将原始值移到 后面,如果是自引用,.await那将是灾难性的。fut

Future该错误来自编译器未能找到for the expression的实现&mut fut。impl<'_, F> Future for &'_ mut F where F: Future + Unpin + ?Sized标准库中有;但是,fut没有实现Unpin,所以这就是编译器在错误消息中报告的内容。

tokio::pin!(fut)首先移动fut(这就是宏如何确保它拥有未来的所有权),然后声明一个新类型fut的变量Pin<&mut F>(其中F是原始fut类型)。

tokio::pin!(fut)因此, uses下面的代码fut操作对未来的固定引用,而不是直接操作未来值。如果我们尝试移动fut,那么我们只需移动固定的参考。如果我们采用对 的可变引用fut,那么我们最终会得到对固定引用 ( ) 的引用&mut Pin<&mut F>。这种类型确实实现了,Future因为:

  • &mut F Unpin即使F不实施也实施Unpin
  • Pin<&mut F> 实施 Unpin因为&mut F实施Unpin
  • Pin<&mut F> 实施 Future因为F实施Future
  • &mut Pin<&mut F> 实现, Future因为Pin<&mut F>同时实现Future和Unpin