Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Using async closure as lambda functions is a bit hard

I’m trying to reproduce an error I’m having on my small app in Rust.

I’m trying to use async closure as lambda functions (very tipical in other languages).

But I just found out that this is a bit difficult in Rust:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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

Error:

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`

Code:

/*
[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(())
}

>Solution :

The problem here is putting the lifetime on player_change_name. This function should not accept a lifetime parameter, because it’s too restrictive at this point — the caller has to choose the lifetime, and there is no possible lifetime the caller can choose that will make sense.

The function itself doesn’t capture the lifetime 'a either, so that restriction is also unnecessary.

What you want to say regarding the lifetimes is that "for any possible lifetime 'a, this closure should accept a PlayerMusts<'a> and return a future that captures 'a. You need a higher-rank trait bound or HRTB:

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

Note the for<'a> syntax, which creates the HRTB.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading