Async Closure Lifetime

Hello, I've recently implemented some redundant code as an async function that takes an async closure. In one of my attempts to call it, I have no problem, but in the other I get the following error:

async block may outlive the current function, but it borrows current_set, which is owned by the current function

I think lifetimes can help me correctly constrain the function, but I don't understand what they should be or where they shoudl be. Here is the minimally reproduced code:

pub async fn operate_on_same_timestamp< Func, Fut>(rows : Vec<PgresRow>, mut operator : Func) -> Result<(), Status>
where   
    Func : FnMut(Vec<PgresRow>) -> Fut,
    Fut  : Future<Output = Result<(), Status>>
{                                                               
    let mut next_set = rows;                                    
    while next_set.len() != 0
    {                                                           
        let first_date_time = next_set[0].get::<&str, DateTime<Utc>>("session_time");
        let (current_set, rest_set) : (Vec<PgresRow>, Vec<PgresRow>) = next_set.into_iter().partition(|row| { row.get::<&str, DateTime<Utc>>("session_time") == first_date_time });
                                                                
        next_set = rest_set;                                    
                                                                
        operator(current_set).await?;                           
    }                                                           
    Ok(())
}

And the function that calls it:

async fn append_similar_times(
    &self,
    row_set: Vec<PgresRow>,
) -> Result<Vec<ProposedSession>, Status> {
    let return_set = Arc::new(Mutex::new(vec![]));
    operate_on_same_timestamp(row_set, |current_set| async {
        let return_set = return_set.clone();
        let mut return_set = return_set.lock().await;

        let date_time = grpc_auto_error!(
            tonic::Code::OutOfRange,
            current_set[0].try_get("session_time")
        );

        let date_time = grpc_auto_error!(
            tonic::Code::InvalidArgument,
            convert_from_sql_timestamp(&date_time)
        );
        let inner_date = date_time.date.as_ref().ok_or(Status::new(
            tonic::Code::InvalidArgument,
            "Date option was not available",
        ))?;
        let inner_time = date_time.time.as_ref().ok_or(Status::new(
            tonic::Code::InvalidArgument,
            "Hour option was not available",
        ))?;
        let month = inner_date.month;
        let year = inner_date.year;
        let day = inner_date.day;
        let hour = inner_time.hour;
        let minute = inner_time.minute;

        let coach_id =
            grpc_auto_error!(tonic::Code::OutOfRange, current_set[0].try_get("coach_id"));
        let duration_minutes = grpc_auto_error!(
            tonic::Code::OutOfRange,
            current_set[0].try_get("session_duration")
        );
        let cost_session_cents = grpc_auto_error!(
            tonic::Code::OutOfRange,
            current_set[0].try_get("price_cents")
        );

        return_set.push(ProposedSession {
            proposed_session_id: vec![],
            coach_id,
            location: None,
            date_time: Some(DateTimeSession {
                date: Some(Date { month, year, day }),
                time: Some(Time { hour, minute }),
            }),
            duration_minutes,
            session_types: vec![],
            cost_session_cents,
        });

        //Add location data

        if let Some(last_element) = return_set.last_mut() {
            for row in current_set.iter() {
                let prop_sess_id =
                    grpc_auto_error!(tonic::Code::OutOfRange, row.try_get("proposed_session_id"));
                let sess_types =
                    grpc_auto_error!(tonic::Code::OutOfRange, row.try_get("session_string"));
                last_element.proposed_session_id.push(prop_sess_id);
                last_element.session_types.push(sess_types);
            }
        }
        Ok(())
    })
    .await?;

    let return_set = return_set.lock().await;
    Ok(return_set.clone())
}

The full error message:

error[E0373]: async block may outlive the current function, but it borrows `current_set`, which is owned by the current function
   --> src/query_interface.rs:71:64
    |
71  |           operate_on_same_timestamp(row_set, |current_set| async {
    |  ________________________________________________________________^
72  | |
73  | |                                                                 let return_set = return_set.clone();
74  | |                                                                 let mut return_set = return_set.lock().await;
75  | |
76  | |                                                                 let date_time = grpc_auto_error!(tonic::Code::OutOfRange, current_set[0]....
    | |                                                                                                                           ----------- `current_set` is borrowed here
...   |
114 | |                                                                 Ok(())
115 | |                                                             }).await?;
    | |_____________________________________________________________^ may outlive borrowed value `current_set`
    |
note: async block is returned here
   --> src/query_interface.rs:71:58
    |
71  |           operate_on_same_timestamp(row_set, |current_set| async {
    |  __________________________________________________________^
72  | |
73  | |                                                                 let return_set = return_set.clone();
74  | |                                                                 let mut return_set = return_set.lock().await;
...   |
114 | |                                                                 Ok(())
115 | |                                                             }).await?;
    | |_____________________________________________________________^

Please post the lifetime error. (the full error as outputted from cargo build)

Also, please reformat that second code block. It's very hard to read when it's indented that far. You can pass the code to rustfmt to do it automatically.

1 Like

Okay, formatted as you requested. Thank you for your help in this matter.

Hello, I've gone ahead and found another way to write this code. Interestingly enough, in other parts of the code, a similar example works fine. I think there might be a bug, but I'm not willing to pursue it at this time. Thank you anyway.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.