为什么递归异步函数在 Rust 中需要"静态参数"?



给定一个简单的异步函数:

async fn foo(n: usize) -> usize {
if n > 0 { foo(n - 1).await }
else { 0 }
}

编译器抱怨必须重写async fn才能返回带盒dyn Future


| async fn foo(n: usize) -> usize {
|                           ^^^^^ recursive `async fn`
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`.
For more information about this error, try `rustc --explain E0733`.

编译器说明 (rustc --explain E0733(:

要实现异步递归,async fn需要脱糖 使得Future在返回类型中是显式的:

use std::future::Future;
fn foo_desugared(n: usize) -> impl Future<Output = ()> {
async move {
if n > 0 {
foo_desugared(n - 1).await;
}
}
}

最后,未来被包裹在一个固定的盒子里:

use std::future::Future;
use std::pin::Pin;
fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
Box::pin(async move {
if n > 0 {
foo_recursive(n - 1).await;
}
})
}

Box<...>确保结果具有已知大小, 并且需要引脚才能将其保存在内存中的同一位置。


现在考虑以下代码:

fn foo(n: &usize) -> Pin<Box<dyn Future<Output = usize>>> {
Box::pin(async move {
if *n > 0 {
foo(&n).await
} else {
0
}
})
}

编译器抱怨&n的生存期应该'static

|   fn foo(n: &usize) -> Pin<Box<dyn Future<Output = usize>>> {
|             ------ help: add explicit lifetime `'static` to the type of `n`: `&'static usize`
| /     Box::pin(async move {
| |         if *n > 0 {
| |             foo(&n).await
| |         } else {
| |             0
| |         }
| |     })
| |______^ lifetime `'static` required

请帮助我了解发生了什么

除非另有说明,否则 Trait 对象 (dyn Trait( 默认具有静态生存期。因此,没有指定生命周期的盒装期货(和其他特征(不能依赖于借用的数据,除非该数据是在'static生命周期内借入的。(这就是您的错误消息所抱怨的(。

要解决此问题,您可以显式指定生存期,也可以只使用'_,在这种情况下,它将使用n: &usize参数的省略生存期:

//        .--  will use elided lifetime from here
//        v                      v--  give the future a non-'static lifetime
fn foo(n: &usize) -> Pin<Box<dyn '_ + Future<Output = usize>>> {
// or: fn foo<'a>(n: &'a usize) -> Pin<Box<dyn 'a + Future<Output = usize>>>
Box::pin(async move {
if *n > 0 {
foo(&n).await // <-- no error, as the future lifetime now depends on the
//     lifetime of n instead of having a 'static lifetime
} else {
0
}
})
}

最新更新