Rust in Everything
基于 async-stream 0.3.6

宏展开

这一部分讲主线的编译期一段:你写下的 stream! { ... } 最后变成了什么代码。代码几乎全在 async-stream-impl/src/lib.rs 一个文件里,316 行,加上 async-stream/src/lib.rs 里两个各 5 行的 macro_rules!。每章都用 cargo expand 实际展开一次,对照源码看每一段输出从哪来。

三章按宏处理输入的顺序排列:

  1. 外壳:stream! 与 stream_inner:macro_rules! 外壳只做一件事,把 ($crate) 放在最前面交给 proc-macro,这样被别的 crate 重新导出时路径也对;stream_inner 和 try_stream_inner 只差一个布尔值;输出的骨架是 pair() 加 AsyncStream::new(rx, async move { '标签: { ... } });一次都没 yield 时插进去的那句 send(()),让空的 stream! 的 Item 成了 ()。
  2. Scrub:改写 yield 与 ?:VisitMut 的四个分支。yield v 变成带标签检查的 __yield_tx.send(v).await;? 变成 match,Err 时发出去再 return,而且不看是不是 try_stream!;闭包和 async 块原样跳过;宏调用的 token 里也找 yield,所以 select! 分支里能写,嵌套的 stream! 则跳过。
  3. for await:syn 解析不了 for await,先在 token 层换成 #[await_] for,再在 ForLoop 分支里展开成 loop { match next(&mut pinned).await { ... } };Pin::new_unchecked 为什么成立;'label: for await 在 0.3.6 里编译不过。
flowchart TB
  in["stream! { ... }"] --> shell["macro_rules!:($crate) + 原样的 token"]
  shell --> parse["parse_input:取出 crate 路径"]
  parse --> rfa["replace_for_await:token 层"]
  rfa --> stmts["Block::parse_within:语句列表"]
  stmts --> scrub["Scrub:yield / ? / #[await_] for / 宏调用"]
  scrub --> out["pair() + AsyncStream::new(rx, async move { ... })"]

💬 关联讨论

发起讨论
加载中...