tokio tutorial for select!个州:

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

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

let fut = example(); // example is an async fn
(&mut fut).await;

并显示以下错误消息:

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`

锁定future 解决了问题:

let fut = example(); // example is an async fn
tokio::pin!(fut);
(&mut fut).await;

为什么有必要为了等待future 的引用而将future 固定下来?

推荐答案

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

让我们先回答一下:为什么要通过价值来等待一个future ,就必须有not个呢?也就是说,为什么这样做是可行的:

pub async fn foo() {
    let fut = async {};
    fut.await;
}

很简单:fut.await等于fut.

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

我们没有机会在fut.await之后移动fut,所以我们不可能在fut上违反Pin的规则.

然而,如果我们可以引用.await,那么我们would就能够将原始值移动到.await之后,如果fut是自引用的,那将是灾难性的.

该错误来自于编译器未能找到表达式&mut futFuture的实现.标准库中有impl<'_, F> Future for &'_ mut F where F: Future + Unpin + ?Sized个;然而,fut没有实现Unpin,所以这就是编译器在其错误消息中报告的内容.

tokio::pin!(fut)首先移动fut(这是宏确保其拥有future 所有权的方式),然后声明一个Pin<&mut F>类型的新变量fut(其中F是原始fut的类型).

因此,使用futtokio::pin!(fut)后面的代码将pinned reference运算到future ,而不是直接计算future 值.如果我们try 移动fut,那么我们只移动固定的引用.如果我们采用对fut的可变引用,那么我们最终得到对固定引用的引用(&mut Pin<&mut F>).此类型does实现Future,因为:

  • &mut F implements Unpin即使F不实现Unpin
  • Pin<&mut F> implements Unpin,因为&mut F实现了Unpin
  • Pin<&mut F> implements Future,因为F实现了Future
  • &mut Pin<&mut F> implements Future,因为Pin<&mut F>同时实现FutureUnpin

Rust相关问答推荐

如何使用Match比较 struct 中的值

限制未使用的泛型导致编译错误

在不重写/专门化整个函数的情况下添加单个匹配手臂到特征的方法?

为什么允许我们将可变引用转换为不可变引用?

为什么这是&q;,而让&q;循环是无限循环?

无法从流中读取Redis请求

在0..1之间将U64转换为F64

我们能确定Rust会优化掉Clone()吗?如果它会立即掉落?

返回Result<;(),框<;dyn错误>>;工作

为什么BufReader实际上没有缓冲短寻道?

`actix-web` 使用提供的 `tokio` 运行时有何用途?

unwrap 选项类型出现错误:无法移出共享引用后面的*foo

如何为已实现其他相关 std trait 的每个类型实现一个 std Trait

枚举的利基优化如何在 Rust 中工作?

内部值发生变化时 Rc 的行为

只有一个字符被读入作为词法分析器的输入

你能告诉我如何在 Rust 中使用定时器吗?

在空表达式语句中移动的值

提取 struct 生成宏中字段出现的索引

Rust 为什么 (u32, u32) 的枚举变体的大小小于 (u64)?