Abstracting over smart pointers/r2d2 pools

Hi all,

I'm trying to create an abstraction over r2d2 pools for Postgres connections: I need it as the setup when running locally differs from the setup in staging/prod. For that reason, I need two different kinds of r2d2 pools, even though they both return a smart pointer to a Postgres connection when called.

As I'm rather new to Rust, this lead me down a rabbit hole trying to get the type checker happy. I ended up boxing the smart pointers, but wonder if this is optimal. Does this code look sensible, or is there a better way to pass things that deref(mut) to the same thing?

use std::ops::DerefMut;
// imports to get make the r2d2_postgres impl happy
use postgres::tls::{MakeTlsConnect, TlsConnect};
use r2d2_postgres::PostgresConnectionManager;
use tokio_postgres::Socket;

pub type PGDerefConn = DerefMut<Target = postgres::Client>;

pub trait PostgresPool {
    fn get_conn(&self) -> Result<Box<PGDerefConn>, r2d2::Error>;
}

impl<T> PostgresPool for r2d2::Pool<PostgresConnectionManager<T>>
where
    T: MakeTlsConnect<Socket> + Clone + 'static + Sync + Send,
    T::TlsConnect: Send,
    T::Stream: Send,
    <T::TlsConnect as TlsConnect<Socket>>::Future: Send,
{
    fn get_conn(&self) -> Result<Box<PGDerefConn>, r2d2::Error> {
        self.get().map(|x| Box::new(x) as Box<PGDerefConn>)
    }
}

pub fn test(pool: &PostgresPool) {
    let mut client = pool.get_conn().unwrap();
    client
        .execute("INSERT INTO foo (bar) VALUES ($1)", &[&1])
        .unwrap();
}

I don't worry too much about dynamic dispatch here; I'd rather have PostgresPool as a trait, even though I know an enum could be slightly faster.

You could make PGDerefConn an associated type on the PostgresPool trait

pub trait PostgresPool {
    type PGDerefConn: DerefMut<Target = postgres::Client>;
    fn get_conn(&self) -> Result<Self::PGDerefConn, r2d2::Error>;
}

This will work as long as you don't want to use PostgresPool behind a Box<dyn PostgresPool>. (Trait objects requite associated type to be specified concretely.)
Otherwise Box is the best way to handle Trait Objects.

On a sidenote

pub type PGDerefConn = DerefMut<Target = postgres::Client>;

is an old deprecated syntax that will start emitting warning soon. Prefix a trait with dyn when using it as a type.

pub type PGDerefConn = dyn DerefMut<Target = postgres::Client>;
1 Like