1.Future
Trait
Future
代表一个可能在未来某个时间点完成的计算。它的定义如下:
rust
use std::pin::Pin;
use std::task::{Context, Poll};
pub trait Future {
type Output; // 关联类型,表示 Future 解析后的结果类型
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
Output
:表示Future
最终的返回值类型。poll
方法:- 传入
Pin<&mut Self>
确保Future
不会在内存中被移动。 - 传入
Context
,用于通知异步运行时何时重新检查Future
的状态。 - 返回
Poll<Self::Output>
,其值可能是:Poll::Pending
:表示Future
仍在进行。Poll::Ready(value)
:表示Future
已完成,并返回计算结果。
- 传入
Rust 编译器在处理 async/.await
语法时,会自动调用 poll
方法,使得 Future
被异步运行时调度。
2.Pin
和 Unpin
Traits
2.1.为什么需要 Pin
?
在 Rust 中,大多数类型可以自由移动,但有些类型(例如包含自引用的结构体)在被移动时会导致未定义行为。因此,我们需要 Pin
机制来确保某些类型在内存中的位置固定不变。
2.2.Pin
作用
Pin<T>
是一个封装类型,它用于防止某些值在内存中被移动。例如:
rust
use std::pin::Pin;
let mut data = String::from("hello");
let pinned = Pin::new(&mut data); // 现在 `data` 不能再被安全地移动
2.3.Unpin
作用
Unpin
是一个标记 trait,表示一个类型可以安全移动。- Rust 默认为大多数类型自动实现
Unpin
。 - 只有需要固定内存位置的类型(如
Future
状态机)才会!Unpin
,意味着必须使用Pin
来确保它们不会被移动。
示例:
rust
use std::marker::Unpin;
struct MyType;
impl Unpin for MyType {} // 手动实现 Unpin,表示该类型可以安全移动
如果一个 Future
不能 Unpin
,那么在 poll
之前必须使用 Pin
来固定它。
3.Stream
Trait
Stream
类似于 Iterator
,但它是异步的,适用于异步数据流的场景。例如网络数据接收、文件读取等。
3.1.Stream
定义
rust
use std::pin::Pin;
use std::task::{Context, Poll};
pub trait Stream {
type Item;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
}
Item
:表示流中的数据类型。poll_next
方法:- 返回
Poll::Pending
,表示暂时没有数据。 - 返回
Poll::Ready(Some(item))
,表示有新数据。 - 返回
Poll::Ready(None)
,表示流已经结束。
- 返回
3.2.StreamExt
Trait
StreamExt
是 futures
crate 提供的一个扩展 trait,它为 Stream
增加了一些便捷方法,例如 next()
:
rust
use futures::stream::{self, StreamExt};
#[tokio::main]
async fn main() {
let mut my_stream = stream::iter(vec![1, 2, 3]);
while let Some(value) = my_stream.next().await {
println!("Received: {}", value);
}
}
stream::iter(vec![1, 2, 3])
创建一个包含1, 2, 3
的流。my_stream.next().await
异步地等待流中的下一个值。StreamExt
提供next()
方法,使Stream
更加易用。
4.实践中的注意事项
-
使用
.await
自动调用poll
- 在异步函数中,
await
关键字会自动调用poll
,无需手动轮询Future
。
- 在异步函数中,
-
当
Future
不是Unpin
时,需要Pin
- 例如,手动
poll
一个Future
时,需要先使用Pin::new()
进行固定。
- 例如,手动
-
Stream
需要StreamExt
提供额外功能- 例如,
StreamExt::next()
提供了await
友好的 API,使Stream
变得更易用。
- 例如,
5.总结
Future
代表异步计算,poll
方法用于轮询其状态。Pin
防止数据被移动,Unpin
表示数据可以自由移动。Stream
代表异步数据流,poll_next
用于获取下一个数据。StreamExt
提供了一些辅助方法,让Stream
更易于使用。
理解这些概念后,我们可以更高效地编写 Rust 异步代码,并充分利用 Rust 语言的内存安全和并发特性。