提问人:Fred Hors 提问时间:3/29/2023 最后编辑:Fred Hors 更新时间:3/29/2023 访问量:74
将异步闭包用作 HRTB 的 lambda 函数
Using async closure as lambda functions with HRTB
问:
我正在尝试使用异步闭包作为 lambda 函数。
但我刚刚发现,还有一些我不明白的东西:
REPL:https://www.rustexplorer.com/b/w6fmvw。
错误:
error: lifetime may not live long enough
--> src/main.rs:101:17
|
94 | async fn handle(&self, input: &PlayerChangeNameInput, whoami: &str) -> Result<Player, ()> {
| - lifetime `'life1` defined here
...
101 | / Box::pin(async {
102 | | let o = Player {
103 | | id: musts.actual.id.clone(),
104 | | name: input.name.to_string(),
... |
107 | | Ok(o)
108 | | })
| |__________________^ returning this value requires that `'life1` must outlive `'static`
法典:
/*
[dependencies]
async-trait = { version = "0.1.68" }
tokio = { version = "1.26.0", features = ["macros", "rt-multi-thread"] }
*/
use std::{future::Future, pin::Pin, sync::Arc};
trait Trait: Send + Sync + PlayerTrait {}
impl<T: PlayerTrait> Trait for T {}
#[async_trait::async_trait]
trait PlayerTrait: Send + Sync {
async fn player_change_name(
&self,
input: &PlayerChangeNameInput,
lambda: &(dyn for<'a> Fn(
PlayerMusts<'a>,
) -> Pin<Box<dyn Future<Output = Result<Player, ()>> + Send + 'a>>
+ Sync),
) -> Result<Player, ()>;
}
struct PlayerChangeNameInput {
pub name: String,
}
struct Player {
pub id: String,
pub name: String,
}
struct PlayerMusts<'a> {
pub actual: &'a Player,
// pub other: &'a str,
}
#[async_trait::async_trait]
impl PlayerTrait for Repo {
async fn player_change_name(
&self,
input: &PlayerChangeNameInput,
lambda: &(dyn for<'a> Fn(
PlayerMusts<'a>,
) -> Pin<Box<dyn Future<Output = Result<Player, ()>> + Send + 'a>>
+ Sync),
) -> Result<Player, ()> {
// I need input here
dbg!(&input.name);
// 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(),
};
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)
}
}
struct HandlerImpl {
repo: Arc<Repo>,
}
fn new_handler(repo: Arc<Repo>) -> Box<dyn Handler> {
Box::new(HandlerImpl { repo })
}
#[async_trait::async_trait]
trait Handler: Send + Sync {
async fn handle(&self, input: &PlayerChangeNameInput, whoami: &str) -> Result<Player, ()>;
}
#[async_trait::async_trait]
impl Handler for HandlerImpl {
async fn handle(&self, input: &PlayerChangeNameInput, whoami: &str) -> Result<Player, ()> {
// use whoami here...
dbg!(whoami);
let result = self
.repo
.player_change_name(input, &|musts| {
Box::pin(async {
let o = Player {
id: musts.actual.id.clone(),
name: input.name.to_string(),
};
Ok(o)
})
})
.await?;
Ok(result)
}
}
pub struct Repo {
// pub pool: Arc<DbPool>,
}
impl Repo {
pub fn new() -> Self {
Self {}
}
}
#[tokio::main]
async fn main() -> Result<(), ()> {
let db_repo = Arc::new(Repo::new());
let handler = new_handler(db_repo);
let whoami = "Myself";
let fake_input = &PlayerChangeNameInput{name: "Frank".to_string()};
let result = handler.handle(fake_input, whoami).await?;
dbg!(result.name);
Ok(())
}
更新:
我刚刚发现了另一个怪癖:https://www.rustexplorer.com/b/vwnvu9for<'a>
error[E0582]: binding for associated type `Output` references lifetime `'b`, which does not appear in the trait input types
--> src/main.rs:14:29
|
14 | dyn for<'b> Fn(ArgT) -> Pin<Box<dyn Future<Output = Result<ResT, ()>> + Send + 'b>> + Sync;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0582`.
这真的很奇怪......至少可以说。
答: 暂无答案
评论
There are a few things you can do that will incidentally fix this in your code by working around the problem instead of actually solving it using lifetimes.
.例如,如何?