提问人:Fred Hors 提问时间:3/29/2023 更新时间:3/29/2023 访问量:128
使用异步闭包作为 lambda 函数有点困难
Using async closure as lambda functions is a bit hard
问:
我正在尝试重现我在 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(())
}
答:
1赞
cdhowie
3/29/2023
#1
这里的问题是把生命周期放在 .此函数不应接受生存期参数,因为它在这一点上限制性太强 -- 调用方必须选择生存期,并且调用方无法选择有意义的生存期。player_change_name
函数本身也不会捕获生存期,因此也不需要这种限制。'a
关于生存期,你想说的是“对于任何可能的生存期,这个闭包应该接受一个并返回一个捕获 的未来。您需要更高等级的特征绑定或 HRTB:'a
PlayerMusts<'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 好吧...当我在对该代码的回答中应用修复程序时,它也可以工作。我不清楚“问题出在调用者身上”是什么意思,因为在我的答案中应用修复程序后,我无法用任何一个版本的代码重现该问题。
评论