rustrust-sqlx

Generic Pool type sqlx


I'm trying to setup sqlx to produce a Pool with a generic Database type for traits, however, running into issues. I do want to avoid as much as possible using AnyPool, due to multiple points raised in the following reply: https://stackoverflow.com/a/70573732/14896203

My current traits implementation:

use sqlx::postgres::PgRow;
use sqlx::{Database, Error, Pool, Postgres};

pub trait DatabaseTraits {
    fn get_address(&self) -> String;

    async fn connect<DB: Database>(&self) -> Result<Pool<DB>, Error>;

    async fn read(&self, schema: String, table: String) -> Result<PgRow, Error> {
        let conn = self.connect().await?;
        let query_string = format!("SELECT * FROM {}.{}", schema, table);
        let rows = sqlx::query(&query_string).fetch_one(&conn).await?;
        Ok(rows)
    }
}

I have attempted to use DB as a "generic type" with Database as I saw it implements Postgres, SQLite and others, however, I feel like I'm missing significant logical points, since the error I am getting is expected: sqlx::Pool<DB> found sqlx::Pool<Postgres>:

impl DatabaseTraits for PostgresConnection {
    fn get_address(&self) -> String {
        self.address.to_string()
    }

    async fn connect<DB: Database>(&self) -> Result<Pool<DB>, Error>
    {
        let conn: Pool<DB> = PgPoolOptions::new()
            .max_connections(5)
            .connect(&self.get_address())
            .await?.into();

        Ok(conn)
    }
}

full code: https://hastebin.com/share/yiwemedome.rust


Solution

  • The problem lies with how you've defined the connect function:

    async fn connect<DB: Database>(&self) -> Result<Pool<DB>, Error>
    

    This allows the caller of the function to decide what concrete type DB should be - but in your case, it's the implementation that's making this choice. The connect implementation for impl DatabaseTraits for PostgresConnection will always return a sqlx::Pool<Postgres>, and the caller can't change that.

    When your trait needs an implementation-defined placeholder type in this way, the correct tool to use is an associated type:

    pub trait DatabaseTraits {
        type DB: Database;
    
        async fn connect(&self) -> Result<Pool<Self::DB>, Error>;
    
        // ...
    }
    

    You would then set the type in your implementation like this:

    impl DatabaseTraits for PostgresConnection {
        type DB = Postgres;
    
        // ...
    }