将异步闭包用作 HRTB 的 lambda 函数

Using async closure as lambda functions with HRTB

提问人:Fred Hors 提问时间:3/29/2023 最后编辑:Fred Hors 更新时间:3/29/2023 访问量:74

问:

我正在尝试使用异步闭包作为 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`.

这真的很奇怪......至少可以说。

Rust Lambda 闭包特征

评论

0赞 cdhowie 3/29/2023
这与这个问题是同一个问题,目前还没有很好的解决方案。您可以执行一些操作,通过解决问题而不是使用生存期实际解决问题来顺便在代码中修复此问题。
0赞 Chayim Friedman 3/29/2023
它可以通过添加生存期和虚拟参数来解决,play.rust-lang.org/...
0赞 Fred Hors 3/29/2023
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..例如,如何?
0赞 Fred Hors 3/29/2023
@ChayimFriedman谢谢。但为什么要进行这种黑客攻击?是否有问题需要在 Rust github 上订阅以关注此主题的新内容?
0赞 Fred Hors 3/29/2023
伙计们,更新的问题中的错误呢?

答: 暂无答案