A bit of a lifetime issue in a trait

Greetings, I'm trying to have a trait that can be implemented in any sqlx executor. I'm using Postgres, so in my case these would be &PgPool and &mut PgConnection. The use case here is that I want to be able to have certain methods work on both a &PgPool and a &mut PgTransaction instance, where the latter implements Deref + DerefMut<Target = PgConnection>.

This is how I'm trying to approach this:

Cargo.toml:

[dependencies.sqlx]
version = "0.8.6"
features = [
    "bigdecimal",
    "chrono",
    "json",
    "postgres",
    "runtime-tokio",
    "tls-native-tls",
]
use sqlx::{Error, PgConnection, PgExecutor, PgPool};

trait Schema: Clone + Send + Sync + 'static
where
    for<'a> &'a mut Self::Connection: DbInteraction<'a>,
    for<'a> &'a Self: DbInteraction<'a>,
{
    type Connection;
    type Transaction: Deref + DerefMut<Target = Self::Connection>;

    async fn begin_transaction(&self) -> Result<Self::Transaction, Error>;
}

impl Schema for PgPool {
    type Connection = PgConnection;

    type Transaction = sqlx::Transaction<'static, sqlx::Postgres>;

    async fn begin_transaction(&self) -> Result<Self::Transaction, Error> {
        Ok(self.begin().await?)
    }
}

async fn use_schema<P: Schema>(pool: P) -> Result<(), Error> {
    // let mut txn = pool.begin_transaction().await?;
    // let res = pool.get_an_integer().await?;
    Ok(())
}

trait DbInteraction<'a> {
    async fn get_an_integer(self) -> Result<i64, Error>;
}

impl<'a, E: PgExecutor<'a>> DbInteraction<'a> for E {
    async fn get_an_integer(self) -> Result<i64, Error> {
        let res: i64 = sqlx::query_scalar("SELECT scalar FROM some_table")
            .fetch_one(self)
            .await?;

        Ok(res)
    }
}

This does not work, I'm getting the following error: the trait bound `for<'a> &'a mut <P as Schema>::Connection: Executor<'_>` is not satisfied.

To me, this indicates that the trait resolver does not tie together the lifetime of PgExecutor and DbInteraction, which I think it should, based on the following: impl<'a, E: PgExecutor<'a>> DbInteraction<'a> for E.

It's also strange that impl Schema for PgPool in itself is not a problem, it only reports an error when I try to use the trait.

I'm grateful for any comments and ideas, you can tell me if what I want to do does not make sense in the first place, etc.

To get this topic moving, first, only supertrait bounds are implied elsewhere.

trait Schema: Clone + Send + Sync + 'static
where
    // These are not supertrait bounds -- are not bounds on `Self`
    // (They are bounds on `&mut Self` and `&Self`)
    for<'a> &'a mut Self::Connection: DbInteraction<'a>,
    for<'a> &'a Self: DbInteraction<'a>,

That means you need to repeat the bounds wherever you use Schema.

-async fn use_schema<P: Schema>(pool: P) -> Result<(), Error> {
+async fn use_schema<P: Schema>(pool: P) -> Result<(), Error>
+where
+    for<'a> &'a mut Self::Connection: DbInteraction<'a>,
+    for<'a> &'a Self: DbInteraction<'a>,
+{
     // let mut txn = pool.begin_transaction().await?;
     // let res = pool.get_an_integer().await?;
     Ok(())
 }

In my attempted mock up, I also had to do this:

    let mut txn = pool.begin_transaction()?;
//  vvvvvvvvvvv
    (&mut *txn).method();
    Ok(())

which is another thing you could try (after adding the bounds).


If those changes still leave you with errors, please post the full error output of cargo build from the console (not your IDE).

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.