24.2-递归
递归
1分钟内可阅读完
译文 · 基于 Asynchronous Programming in Rust
递归
原文链接: https://rust-lang.github.io/async-book/07_workarounds/04_recursion.html
在内部,async fn 会创建包含每个被 .await 的子 Future 的状态机类型。这使得递归 async fn 有点棘手,因为生成的状态机类型必须包含自身:
# async fn step_one() { /* ... */ }
# async fn step_two() { /* ... */ }
# struct StepOne;
# struct StepTwo;
// 此函数:
async fn foo() {
step_one().await;
step_two().await;
}
// 生成类似这样的类型:
enum Foo {
First(StepOne),
Second(StepTwo),
}
// 因此此函数:
async fn recursive() {
recursive().await;
recursive().await;
}
// 生成类似这样的类型:
enum Recursive {
First(Recursive),
Second(Recursive),
}
这行不通——我们创建了无限大小的类型!编译器会报错:
error[E0733]: recursion in an async fn requires boxing
--> src/lib.rs:1:1
|
1 | async fn recursive() {
| ^^^^^^^^^^^^^^^^^^^^
|
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
要允许这一点,我们必须使用 Box 引入间接层。
在 Rust 1.77 之前,由于编译器限制,仅将 recursive() 的调用包装在 Box::pin 中是不够的。要使其工作,我们必须将 recursive 改为返回 .boxed() async 块的非 async 函数:
use futures::future::{BoxFuture, FutureExt};
fn recursive() -> BoxFuture<'static, ()> {
async move {
recursive().await;
recursive().await;
}.boxed()
}
在较新版本的 Rust 中,[该编译器限制已解除]。
自 Rust 1.77 起,带分配间接层的 async fn 递归支持[已稳定],因此只要使用某种形式的间接层以避免函数状态的无限大小,就允许递归调用。
这意味着像这样的代码现在可以工作:
async fn recursive_pinned() {
Box::pin(recursive_pinned()).await;
Box::pin(recursive_pinned()).await;
}