rust, future & generator
- future trait
- fn() -> impl Future
- async fn() 以及闭包
- async {}
- await
探讨future的不同写法。以及其中的差异。
future trait
自定义实现一个 trait 特征。需要实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | pub trait Future { /// The type of value produced on completion. #[stable(feature = "futures_api", since = "1.36.0")] type Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; } pub enum Poll<T> { /// Represents that a value is immediately ready. #[stable(feature = "futures_api", since = "1.36.0")] Ready(#[stable(feature = "futures_api", since = "1.36.0")] T), /// Represents that a value is not ready yet. /// /// When a function returns `Pending`, the function *must* also /// ensure that the current task is scheduled to be awoken when /// progress can be made. #[stable(feature = "futures_api", since = "1.36.0")] Pending, } |
Executor 会来调用
fn() -> impl Future
显式返回一个future trait。
1 2 3 | fn my_fut() -> impl Future<Output = ()> { future::ready(()) } |
async fn() 以及闭包
代码示例如下:
1 2 3 | async fn ss() -> u32 { 0 } |
async 关键字,将函数的原型修改为返回一个future trait。然后将执行的结果包装在一个新的future中返回。大致相当于:
1 2 3 | fn ss() -> impl Future<Output = u32> { async { 0 } } |
从这里可以看到,async fn 永远是返回 Ready(T),而没有办法返回 Pending的。
async {}
这个代码块实现了一个匿名的 Future Trait,大致相当于下面的逻辑:
1 2 3 4 5 6 7 | impl Future for noname { type Output = u32; fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> { let ret = { ... }; Poll::Ready(ret) } } |
await
将 async {} 的代码转换生成一个 Future trait。async {} 本身是惰性的,只有在await之后才会真正执行 Future trait。
生成逻辑在标准库中,可以看到是一个generator,名为GenFuture,实现了 Future Trait。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return> where T: Generator<ResumeTy, Yield = ()>, { struct GenFuture<T: Generator<ResumeTy, Yield = ()>>(T); // We rely on the fact that async/await futures are immovable in order to create // self-referential borrows in the underlying generator. impl<T: Generator<ResumeTy, Yield = ()>> !Unpin for GenFuture<T> {} impl<T: Generator<ResumeTy, Yield = ()>> Future for GenFuture<T> { type Output = T::Return; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { // Safety: Safe because we're !Unpin + !Drop, and this is just a field projection. let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) }; // Resume the generator, turning the `&mut Context` into a `NonNull` raw pointer. The // `.await` lowering will safely cast that back to a `&mut Context`. match gen.resume(ResumeTy(NonNull::from(cx).cast::<Context<'static>>())) { GeneratorState::Yielded(()) => Poll::Pending, GeneratorState::Complete(x) => Poll::Ready(x), } } } GenFuture(gen) } |
GenFuture 是一个实现了 Future 的 generator。因此在 Executor 执行这样的 Future 会进入相应的 poll 方法,接下来 gen.resume 将会进入执行 async {} 的内容。