Ahmet Yazıcı
Ahmet Yazıcı

Reputation: 714

Accepting either mutable and immutable generic types with traits in Rust

I am using Rust with sqlx and postgres to build a REST API. I am trying to build a Database struct which has a generic connection param in it.

struct Database<T>
where
    T: Sync + Send,
    for<'a> &'a T: sqlx::Executor<'a, Database = Postgres>,
{
  conn: T
}

T would be owned by the struct, and &T is expected to implement Executor trait.

I am able to use Pool<Postgres> as T, since &Pool<Postgres> implements the Executor.

What I want is (and this is the reason I made the conn a generic type) to be able to use Transaction<Postgres> as T. But the problem is that &Transaction<Postgres> does not implement the Executor trait, but &mut Transaction<Postgres> does.

The reason I want to do this is there are CRUD functions that I want to be able to use with both a transaction and a pool connection. And I don't want to write duplicate code. How can I achieve this?

Upvotes: 0

Views: 187

Answers (1)

Alexey Romanov
Alexey Romanov

Reputation: 170815

Maybe someone has a better idea, but it seems the following could work.

use std::marker::PhantomData;
use sqlx::{Pool, Transaction, Postgres};

trait Connection<'a>: Sync + Send {
    type Executor: sqlx::Executor<'a>;
    fn executor(&'a mut self) -> Self::Executor;
}

impl <'a> Connection<'a> for Pool<Postgres> {
    type Executor = &'a Self;
    fn executor(&'a mut self) -> Self::Executor {
        &*self
    }
}

impl <'c> Connection<'c> for Transaction<'c, Postgres> {
    type Executor = &'c mut Self;
    fn executor(&'c mut self) -> Self::Executor {
        self
    }
}

struct Database<'a, T: Connection<'a>>
{
    conn: T,
    phantom: PhantomData<&'a T>
}

impl <'a, T: Connection<'a>> Database<'a, T> {
    fn q(&'a mut self) {
        let _: T::Executor = self.conn.executor();
        // sqlx::query!("Some query").execute(self.conn.executor())
    }
}

At the very least it compiles :)

Upvotes: 0

Related Questions