`Self does not live long enough` generic associated types with lifetimes in async traits

82 Views Asked by At

I'm facing a problem with generic associated types and asynchronous rust.

The problem is: I want to be able to create a function called transaction in a trait Connection . This method basically creates a transaction and sends it in a callback function. If the callback returns Ok, we commit the transaction. Otherwise, we roll back. Simple, right? Here’s the code:

use anyhow::Result;
use futures::future::BoxFuture;

#[async_trait::async_trait]
trait Transaction: Send + Sync {
    async fn commit(self) -> Result<()>;

    async fn rollback(self) -> Result<()>;

    async fn query(
        &mut self,
        sql: &str,
    ) -> Result<()>;
}

#[async_trait::async_trait]
trait Connection: Send + Sized {
    type Tx<'t>: Transaction
    where
        Self: 't;

    async fn begin(&self) -> Result<Self::Tx<'_>>;

    fn transaction<'a, F, R, E>(
        &'a mut self,
        callback: F,
    ) -> BoxFuture<Result<R, E>>
    where
        for<'c> F:
            FnOnce(&'c mut Self::Tx<'_>) -> BoxFuture<'c, Result<R, E>> + 'a + Send + Sync,
        R: Send,
        E: From<anyhow::Error> + Send,
    {
        Box::pin(async move {
            let mut tx = self.begin().await?;
            let ret = callback(&mut tx).await;

            match ret {
                Ok(ret) => {
                    tx.commit().await?;
                    Ok(ret)
                }
                Err(err) => {
                    tx.rollback().await?;
                    Err(err)
                }
            }
        })
    }
}

#[tokio::main]
async fn main() -> Result<()> { Ok(()) }

The problem is that I want to use a generic associated type in this trait called Tx that defines the transaction type.

The associated type has a generic lifetime ‘t, because the transaction is tied to the connection. That’s where the problem resides and it throws the error Self does not live long enough

Does anyone have any suggestions on top of that?

I tried to remove the generic associated type like sqlx does, and it works. However I need to use a GAT.

Edited

I found an answer to make it compile: change the return type of the function from BoxFuture to LocalBoxFuture.

That makes the code above compile fine, but that's not a reasonable solution for my current scenario because I need to guarantee Sendness. So, I'm looking for alternatives.

0

There are 0 best solutions below