`connection` does not live long when passed to async closure

I'm trying to pass an async closure to a function which will start a transaction then call the closure then commit the transaction

impl<'t> Transactional<'t> {
    pub async fn run<'result, F>(self, f: F) -> Result<(), anyhow::Error>
    where
        F: Send + FnOnce(TransactionalCtx<'t>) -> BoxFuture<'t, Result<(), anyhow::Error>> + 't,
    {
        match self {
            Transactional::Postgres(mut conn) => {
                let mut txn = conn.transaction().await.map_err(|e| anyhow!(e))?;
                f(TransactionalCtx::Postgres(&txn)).await?;

                txn.commit().await.map_err(|e| anyhow!(e))?;
            }
            // Transactional::Oracle(conn) => {
            //     f(TransactionalCtx::Oracle(&conn)).await?;
            //     let commit = conn.commit();
            //     if commit.is_err() {
            //         conn.rollback();
            //         commit.map_err(|e| anyhow!(e))?;
            //     }
            // }
            Transactional::Oracle(..) => todo!(),
            Transactional::Mssql() => todo!(),
        }

        Ok(())
    }
}

#[derive(Clone)]
pub enum TransactionalCtx<'t> {
    Postgres(&'t bb8_postgres::tokio_postgres::Transaction<'t>),
    Oracle(&'t bb8_oracle::oracle::Connection),
    Mssql(),
}

compiler output

error[E0597]: `conn` does not live long enough
   --> payments-processor/src/db/mod.rs:197:31
    |
190 | impl<'t> Transactional<'t> {
    |      -- lifetime `'t` defined here
...
197 |                 let mut txn = conn.transaction().await.map_err(|e| anyhow!(e))?;
    |                               ^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
198 |                 f(TransactionalCtx::Postgres(&txn)).await?;
    |                 ----------------------------------- argument requires that `conn` is borrowed for `'t`
...
201 |             }
    |             - `conn` dropped here while still borrowed

error[E0597]: `txn` does not live long enough
   --> payments-processor/src/db/mod.rs:198:46
    |
190 | impl<'t> Transactional<'t> {
    |      -- lifetime `'t` defined here
...
198 |                 f(TransactionalCtx::Postgres(&txn)).await?;
    |                 -----------------------------^^^^--
    |                 |                            |
    |                 |                            borrowed value does not live long enough
    |                 argument requires that `txn` is borrowed for `'t`
...
201 |             }
    |             - `txn` dropped here while still borrowed

error[E0505]: cannot move out of `txn` because it is borrowed
   --> payments-processor/src/db/mod.rs:200:17
    |
190 | impl<'t> Transactional<'t> {
    |      -- lifetime `'t` defined here
...
198 |                 f(TransactionalCtx::Postgres(&txn)).await?;
    |                 -----------------------------------
    |                 |                            |
    |                 |                            borrow of `txn` occurs here
    |                 argument requires that `txn` is borrowed for `'t`
199 |
200 |                 txn.commit().await.map_err(|e| anyhow!(e))?;
    |                 ^^^ move out of `txn` occurs here

Hard to be sure without a runable reproduction, but it looks like conn is local, yet you're trying to borrow it for a lifetime that the caller chooses ('t), which can never work.

You can try a higher-ranked bound like

for<'x> FnOnce(TransactionalCtx<'x>) -> BoxFuture<'x, Result<(), anyhow::Error>> + 'x,

The lifetime parameter 'result looks to be unused and uncessary.

2 Likes

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.