Lifetime of slice is too short

Hello everyone

I want to execute prepared statements on my PostgreSQL database and I've got this working code:

for (key, val) in kv {
     let stmt = self.stmts.get(&*key).unwrap();
     let val = val as i64;

    db_tx.execute(stmt, &[date, &val]).await;
}

So I have prepared statements stored in the Vector self.stmts using which I want to store a date and a value into the database for each key-value pair I have (same number as I have prepared statements).

So while this code is exactly doing what I want, it is bad from a performance point of view, because I always await the result of the db until I write the next value. But since this could be done in parallel, I thought it's a nice example to try out join_all are try_join_all or some other feature from async rust. Here's the code I ended up with:

    let mut executes = Vec::new();

    for (key, val) in kv {
        let stmt = self.stmts.get(&*key).unwrap();
        let val = val as i64;

        executes.push(db_transaction.execute(stmt, &[date, &val]));
    }

    try_join_all(executes).await;

Unfortunately I get the following error here:

    |             executes.push(db_transaction.execute(stmt, &[date, &val]));
    |                                                         ^^^^^^^^^^^^  - temporary value is freed at the end of this statement
    | 
    |                                                creates a temporary which is freed while still in use

I've tried many different things but I always end up with an ownership/lifetime problem. The main problem is that the execute method requires a slice for the params, which is inherently borrowed instead of owned, and because consequently it gets out of scope after the for loop.

Thank you everyone for your help, I appreciate it a lot!

You must keep the owner of val alive for the duration of the reference to it. For example, you could make a Vec<i64> before your loop, then in your loop use references into that vector.

Another option is something like this:

let mut executes = Vec::new();

for (key, val) in kv {
    let stmt = self.stmts.get(&*key).unwrap();
    let val = val as i64;

    executes.push(async move {
        db_transaction.execute(stmt, &[date, &val]).await
    });
}

try_join_all(executes).await;

Here, by using an async block with move, we take ownership of val.

With the await being there twice? Would the future be executed then already in the first call, kind of destroying the purpose of the try_join_all().await ?

No, when you use an async block, anything inside it becomes lazy and is not executed until the async block itself is awaited.

Sounds good! But here with the async move block, the compiler complains that db_transaction is being moved in the first iteration of the loop ...

You can try this:

let db_transaction_ref = &db_transaction;
executes.push(async move {
    db_transaction_ref.execute(stmt, &[date, &val]).await
});

It works!!!!! Thank you thank you thank you Alice!!
That would have taken me another week to figure out hehehe