|
|
|
@ -1 +1,70 @@
|
|
|
|
|
## 深入理解 async 相关的 traits
|
|
|
|
|
|
|
|
|
|
> [ch17-05-traits-for-async.md](https://github.com/rust-lang/book/blob/main/src/ch17-05-traits-for-async.md)
|
|
|
|
|
> <br>
|
|
|
|
|
> commit 56ec353290429e6547109e88afea4de027b0f1a9
|
|
|
|
|
|
|
|
|
|
贯穿本章,我们通过多种方式使用了 `Future`、`Pin`、`Unpin`、`Stream` 和 `StreamExt` trait。但是直到目前为止,我们避免过多地了解它们如何工作或者如何组合在一起的细节,这对你大多数的日常 Rust 开发是可以的。不过有时你会遇到需要了解更多细节的场景。在本小节,我们会足够深入以便理解这些场景,并仍会将 *真正* 有深度的内容留给其它文档。
|
|
|
|
|
|
|
|
|
|
### `Future` trait
|
|
|
|
|
|
|
|
|
|
让我们以更深入地了解 `Future` trait 作为开始。这里是 Rust 中其如何定义的:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
use std::pin::Pin;
|
|
|
|
|
use std::task::{Context, Poll};
|
|
|
|
|
|
|
|
|
|
pub trait Future {
|
|
|
|
|
type Output;
|
|
|
|
|
|
|
|
|
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
trait 定义中包含一些的新类型和我们之前没有见过新语法,所以让我们一点一点地过一遍这个定义。
|
|
|
|
|
|
|
|
|
|
第一, `Future` 的关联类型 `Output` 表明 future 所生成的类型。这类似于 `Iterator` trait 的关联类型 `Item`。第二,`Future` 还有一个 `poll` 方法,其有一个特殊的 `self` 参数的 `Pin` 引用和一个 `Context` 类型的可变引用,并返回一个 `Poll<Self::Output>`。稍后我们再细说 `Pin` 和 `Context`。现在让我们专注于方法返回的 `Poll` 类型:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
enum Poll<T> {
|
|
|
|
|
Ready(T),
|
|
|
|
|
Pending,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`Poll` 类型类似于一个 `Option`。它有一个包含值的变体 `Ready(T)`,和一个没有值的变体 `Pending`。不过 `Poll` 所代表的意义与 `Option` 非常不同!`Pending` 变体表明 future 仍然还有工作要进行,所有调用者稍后需要再次检查。`Ready` 变体表明 future 已经完成了其工作并且 `T` 的值是可用的。
|
|
|
|
|
|
|
|
|
|
> 注意:对于大部分功能,调用者不应在 future 返回 `Ready` 后再次调用 `poll`。很多 future 在完成后再次轮询会 panic。可以安全地再次轮询的 future 会在文档中显示地说明。这类似于 `Iterator::next` 的行为。
|
|
|
|
|
|
|
|
|
|
当你见到使用 `await` 的代码时,Rust 会在底层将其编译为调用 `poll` 的代码。如果你回头看下示例 17-4,其在一个单个 URL 解析完成后打印出页面标题,Rust 将其编译为一些类似(虽然不完全是)这样的代码:
|
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
match page_title(url).poll() {
|
|
|
|
|
Ready(page_title) => match page_title {
|
|
|
|
|
Some(title) => println!("The title for {url} was {title}"),
|
|
|
|
|
None => println!("{url} had no title"),
|
|
|
|
|
}
|
|
|
|
|
Pending => {
|
|
|
|
|
// But what goes here?
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
如果 future 仍然是 `Pending` 的话我们应该做什么呢?我们需要一些方法来反复尝试并直到 future 最终结束。换句话说,我们需要一个循环:
|
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
let mut page_title_fut = page_title(url);
|
|
|
|
|
loop {
|
|
|
|
|
match page_title_fut.poll() {
|
|
|
|
|
Ready(value) => match page_title {
|
|
|
|
|
Some(title) => println!("The title for {url} was {title}"),
|
|
|
|
|
None => println!("{url} had no title"),
|
|
|
|
|
}
|
|
|
|
|
Pending => {
|
|
|
|
|
// continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
但是如何 Rust 将其编译为正好如此的代码的话,每一个 `await` 都会阻塞 -- 这完全与我们期望的相反!
|
|
|
|
|