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 语言的内存安全和并发特性。