.await 背后到底发生了什么?编译器把 async fn 变成一个枚举状态机,每个 await 点是一个状态,跨 await 的局部变量存在 Future 里。这篇用 size_of_val 直接量出状态机的大小,再验证它如何保存状态、为什么不能递归、为什么需要 Pin。
编译:成功
运行(退出码 0):
async fn nothing() 的 Future 大小: 1 字节
buf 在 await 前用完(仍在作用域内): 1002 字节
buf 放进内层 { } 块,await 前作用域结束: 2 字节
buf[1000] 跨 await 存活: 1002 字节
a[1000] + b[2000] 跨 await 存活: 3002 字节
两个 await,只有一个 1000 字节数组跨第二个 await: 1002 字节
YieldNow 自己: 1 字节Future 与 Poll 见Future 与 Poll。
编译:成功
运行(退出码 0):
第 1 次 poll:
[状态0] 开始
第 2 次 poll:
[await 之后] i = 1 sum = 1
第 3 次 poll:
[await 之后] i = 2 sum = 3
第 4 次 poll:
[await 之后] i = 3 sum = 6
Ready(6),共 poll 4 次 编译:成功
运行(退出码 101):
第一次 poll: Ready(1)
第二次 poll(已完成):
thread 'main' (7203531) panicked at <实验目录>/src/u98c.rs:7:21:
`async fn` resumed after completion
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 源码:
1 | async fn fib(n: u32) -> u32 { if n < 2 { n } else { fib(n - 1).await + fib(n - 2).await } }
2 | fn main() { let _ = fib(5); }
编译:失败
error[E0733]: recursion in an async fn requires boxing
--> <实验目录>/src/u98d.rs:1:1
|
1 | async fn fib(n: u32) -> u32 { if n < 2 { n } else { fib(n - 1).await + fib(n - 2).await } }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ---------------- recursive call here
|
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0733`.
编译:成功
运行(退出码 0):
Ready(55) 源码:
1 | #![allow(unused)]
2 | use std::{future::Future, pin::Pin, task::{Context, Poll, Waker}, mem::size_of_val};
3 | struct YieldNow(bool);
4 | impl Future for YieldNow { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { if self.0 { Poll::Ready(()) } else { self.0 = true; cx.waker().wake_by_ref(); Poll::Pending } } }
5 | fn yield_now() -> YieldNow { YieldNow(false) }
6 |
7 | fn is_unpin<T: Unpin>(_: &T) {}
8 | async fn borrows() { let local = [1u8, 2, 3]; let r = &local; yield_now().await; println!("{}", r[0]); }
9 | fn main() { let f = borrows(); is_unpin(&f); is_unpin(&yield_now()); }
编译:失败
error[E0277]: `{async fn body of borrows()}` cannot be unpinned
--> <实验目录>/src/u98f.rs:9:41
|
8 | async fn borrows() { let local = [1u8, 2, 3]; let r = &local; yield_now().await; println!("{}", r[0]); }
| - within this `impl Future<Output = ()>`
9 | fn main() { let f = borrows(); is_unpin(&f); is_unpin(&yield_now()); }
| -------- ^^ within `impl Future<Output = ()>`, the trait `Unpin` is not implemented for `{async fn body of borrows()}`
| |
| required by a bound introduced by this call
|
= note: consider using the `pin!` macro
consider using `Box::pin` if you need to access the pinned value outside of the current scope
note: required because it appears within the type `impl Future<Output = ()>`
--> <实验目录>/src/u98f.rs:8:19
|
8 | async fn borrows() { let local = [1u8, 2, 3]; let r = &local; yield_now().await; println!("{}", r[0]); }
| ^
note: required by a bound in `is_unpin`
--> <实验目录>/src/u98f.rs:7:16
|
7 | fn is_unpin<T: Unpin>(_: &T) {}
| ^^^^^ required by this bound in `is_unpin`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.Pin 见Pin 与 Unpin。