为什么递归异步函数需要 Rust 中的“静态参数"? [英] Why recursive async functions require 'static parameters in Rust?

查看:55
本文介绍了为什么递归异步函数需要 Rust 中的“静态参数"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

async fn foo(n: usize) ->使用{如果 n >0 { foo(n - 1).await }其他{0}}

编译器抱怨必须重写 async fn 以返回一个装箱的 dyn Future.

<预><代码>|异步 fn foo(n: usize) ->使用{|^^^^^ 递归`async fn`= 注意:必须重写递归`async fn` 以返回装箱的`dyn Future`.有关此错误的更多信息,请尝试 `rustc --explain E0733`.

<小时>

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

要实现异步递归,需要对async fn进行脱糖这样 Future 在返回类型中是显式的:

使用 std::future::Future;fn foo_desugered(n: usize) ->impl Future<输出 = ()>{异步移动{如果 n >0 {foo_desugared(n - 1).await;}}}

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

使用 std::future::Future;使用 std::pin::Pin;fn foo_recursive(n: usize) ->Pin<Box<dyn Future<Output = ()>>{Box::pin(异步移动{如果 n >0 {foo_recursive(n - 1).await;}})}

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

<小时>

现在考虑这个代码:

fn foo(n: &usize) ->Pin<Box<dyn Future<Output = usize>>{Box::pin(异步移动{如果 *n >0 {foo(&n).await} 别的 {0}})}

编译器抱怨 &n 的生命周期应该是 'static.

<代码>|fn foo(n: &usize) ->Pin<Box<dyn Future<Output = usize>>{|------ 帮助:向`n` 的类型添加显式生命周期`'static`:`&'static usize`|/Box::pin(异步移动{||如果 *n >0 {||foo(&n).await||} 别的 {||0||}||})||______^ 需要静态"生命周期

请帮助我了解发生了什么.

解决方案

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

要解决这个问题,您可以明确指定生命周期,也可以仅使用 '_,在这种情况下,它将使用 n 的省略生命周期:&usize 参数:

//.-- 将从这里使用省略的生命周期//v v-- 给未来一个非'静态的生命周期fn foo(n: &usize) ->Pin<Box<dyn'_ + Future<Output = usize>>{//或: fn foo<'a>(n: &'a usize) ->Pin<Box<dyn'a + Future<Output = usize>>Box::pin(异步移动{如果 *n >0 {foo(&n).await//<-- 没有错误,因为未来的生命周期现在取决于//n 的生命周期而不是静态生命周期"} 别的 {0}})}

Given a simple async function:

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

The compiler complains that async fn must be rewritten to return a boxed 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`.


Compiler explanation (rustc --explain E0733):

To achieve async recursion, the async fn needs to be desugared such that the Future is explicit in the return type:

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

Finally, the future is wrapped in a pinned box:

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

The Box<...> ensures that the result is of known size, and the pin is required to keep it in the same place in memory.


Now consider this code:

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

The compiler complains that the lifetime of &n should be '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

Please help me understand what is going on.

解决方案

Trait objects (dyn Trait) will by default have a static lifetime, unless otherwise specified. Because of this, boxed futures (and other traits) without a specified lifetime cannot depend on borrowed data, unless that data is borrowed for the 'static lifetime. (Which is what your error message complains about).

To solve this, you can either specify the lifetime explicitly, or you can just use '_, in which case it will use the elided lifetime of the n: &usize parameter:

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

这篇关于为什么递归异步函数需要 Rust 中的“静态参数"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆