bigywater.tech
Rust · 96

Future 与 Poll

Rust 的 async 不是魔法:Future 就是一个被 poll 的状态机。这篇不借助任何运行时,自己写 poll 循环,把「惰性」「Pending 与 Ready」「await 向上传 Pending」「drop 一个 Future」都亲眼跑出来。

0 行
调用 async fn 但没 poll 时,函数体执行的输出行数
4 次
job() 里两个 await 总共需要的外层 poll 次数
Waker::noop()
本文用来手动 poll 的空 waker(标准库自带)

Future 是惰性的

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    调用 hello():
    拿到了 Future(1 字节),函数体还没有运行
    第一次 poll:
      hello() 的函数体开始执行
    poll 返回 Ready(42)

手写一个 Future

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
      第 1 次 poll → Pending(剩 3)
      第 2 次 poll → Pending(剩 2)
      第 3 次 poll → Pending(剩 1)
      第 4 次 poll → Ready
    最终得到 4

async runtime 做的事见Async Runtime 做了什么。

await 向上传 Pending

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    外层第 1 次 poll:
      A 开始
    → Pending
    外层第 2 次 poll:
    → Pending
    外层第 3 次 poll:
      A 结束
      B 开始
    → Pending
    外层第 4 次 poll:
      B 结束
    → Ready("全部完成")

async fn 生成的状态机见async/await 状态机。

drop 一个 Future

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    创建后直接 drop(没 poll):
    poll 一次后 drop:
      work(half) 开始
      准备 drop Future
      Noisy(half) 被 drop

取消语义见Future 取消与 Drop。

忘记 await

实测 · 本机执行输出
  源码:
      1 | async fn f() -> i32 { 1 }
      2 | async fn g() { f(); }
      3 | fn main() { let _ = g(); }
  编译:成功;警告:
    warning: unused implementer of `Future` that must be used
     --> <实验目录>/src/u96e.rs:2:16
      |
    2 | async fn g() { f(); }
      |                ^^^
      |
      = note: futures do nothing unless you `.await` or poll them
      = note: `#[warn(unused_must_use)]` (part of `#[warn(unused)]`) on by default
    
    warning: 1 warning emitted
  运行(退出码 0):
    (无输出)

分步演示

Future 与 Poll
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。poll 用 Waker::noop(),没有使用任何异步运行时
    • 没有验证:真正的 waker 唤醒(放在 Waker 一篇)、Pin 的作用(放在 Pin 与 Unpin 一篇)、async 块与 async fn 在类型上的区别、Poll::Pending 但不调用 waker 时会发生什么
    京ICP备2026067502号-1京公网安备11010502063487号