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

时间:2019-12-31 02:11:12

标签: rust async-await

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

super

编译器抱怨必须重写MyClass才能返回装箱的super

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
        }
    })
}

请帮助我了解发生了什么情况

1 个答案:

答案 0 :(得分:4)

特质对象(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
        }
    })
}