使用异步闭包作为 lambda 函数有点困难

Using async closure as lambda functions is a bit hard

提问人:Fred Hors 提问时间:3/29/2023 更新时间:3/29/2023 访问量:128

问:

我正在尝试重现我在 Rust 中的小应用程序上遇到的错误。

我正在尝试使用异步闭包作为 lambda 函数(在其他语言中非常有提示性)。

但是我刚刚发现这在 Rust 中有点困难:

REPL:https://www.rustexplorer.com/b/bcwggm

错误:

error: lifetime may not live long enough
  --> src/main.rs:55:9
   |
54 |       let result = player_change_name(&|musts| {
   |                                         ------ return type of closure is Pin<Box<(dyn Future<Output = Result<Player, ()>> + Send + '2)>>
   |                                         |
   |                                         has type `PlayerMusts<'1>`
55 | /         Box::pin(async {
56 | |             let o = Player {
57 | |                 id: musts.actual.id.clone(),
58 | |                 name: "Frank".to_string(),
...  |
62 | |             Ok(o)
63 | |         })
   | |__________^ returning this value requires that `'1` must outlive `'2`

法典:

/*
[dependencies]
tokio = { version = "1.26.0", features = ["macros", "rt-multi-thread"] }
*/

use std::{future::Future, pin::Pin};

#[derive(Debug, Default, Clone)]
pub struct Player {
    pub id: String,
    pub name: String,
    pub team: Option<String>,
}

struct PlayerMusts<'a> {
    pub actual: &'a Player,
    pub other: &'a str,
}

async fn player_change_name<'a>(
    lambda: &(dyn 'a
          + Fn(PlayerMusts) -> Pin<Box<dyn Future<Output = Result<Player, ()>> + Send + 'a>>
          + Sync),
) -> Result<Player, ()> {
    // I need to await for many things in here

    // Eg. for DB connection pool...
    // let mut db_connection = pool.begin().await?;

    // Here I can query the actual player, I'm faking it now...
    let actual = Player {
        id: "1".to_string(),
        name: "Bob".to_string(),
        team: None,
    };

    let worked_player = lambda(PlayerMusts {
        actual: &actual,
        other: "Other",
    })
    .await?;

    // I'm saving the lambda result here, I'm faking it now...
    // let result = db_connection.save(&worked_player, &actual).await?;
    let result = worked_player;

    // db_connection.save_and_close().await?;

    Ok(result)
}

#[tokio::main]
async fn main() -> Result<(), ()> {
    let result = player_change_name(&|musts| {
        Box::pin(async {
            let o = Player {
                id: musts.actual.id.clone(),
                name: "Frank".to_string(),
                team: None,
            };

            Ok(o)
        })
    })
    .await?;

    dbg!(result);

    Ok(())
}
Rust Lambda 闭包

评论


答:

1赞 cdhowie 3/29/2023 #1

这里的问题是把生命周期放在 .此函数不应接受生存期参数,因为它在这一点上限制性太强 -- 调用方必须选择生存期,并且调用方无法选择有意义的生存期player_change_name

函数本身也不会捕获生存期,因此也不需要这种限制。'a

关于生存期,你想说的是“对于任何可能的生存期,这个闭包应该接受一个并返回一个捕获 的未来。您需要更高等级的特征绑定或 HRTB:'aPlayerMusts<'a>'a

async fn player_change_name(
    lambda: &(dyn for<'a> Fn(PlayerMusts<'a>) -> Pin<Box<dyn Future<Output = Result<Player, ()>> + Send + 'a>> + Sync),
) -> Result<Player, ()> {

请注意创建 HRTB 的语法。for<'a>

评论

0赞 Fred Hors 3/29/2023
谢谢。我试过了,但我认为有些东西还是坏了:rustexplorer.com/b/hsdahn
0赞 Fred Hors 3/29/2023
你的建议有效,我需要研究一下。但现在问题出在来电者身上。为什么?
0赞 cdhowie 3/29/2023
@FredHors 该问题中的代码与该问题中的代码完全相同。当我将更改应用于您的代码时,它会成功编译并运行
0赞 Fred Hors 3/29/2023
不,代码略有不同,我为问题引入了特征以更好地重现我的本地问题。
0赞 cdhowie 3/29/2023
@FredHors 好吧...当我在对该代码的回答中应用修复程序时,它也可以工作。我不清楚“问题出在调用者身上”是什么意思,因为在我的答案中应用修复程序后,我无法用任何一个版本的代码重现该问题。