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.

1 Like

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.

1 Like

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.

1 Like

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
});
2 Likes

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

1 Like