Reborrowing via a trait?

Is there a trait that provides a method-shaped version of reference reborrowing?

I'm (again) working with sqlx, where the Executor trait is implemented for three families of types:

  • &mut 't Transaction<…>,
  • &mut 'c Connection<…>, and
  • &Pool<…>.

I'm trying to create a generic struct that can wrap an Executor and provide an app-appropriate interface to it, but I'm also trying to use sqlx::query::Map to generalize access to the schema.

And therein lies my problem. Map::fetch_one and friends all take a parameter of type E (with the constraint E: Executor) by value. I have something that I know to be of type E (with the same constraint), but I don't want to give up ownership of the value in order to call fetch_one with it. I can also assert knowledge that E is a reference rather than a bare value, if need be.

How do I write something equivalent to the line

        let result = R::submit(submission) // Map<…>
            .fetch_one(self.executor)
            .await?;

that does not give up ownership of self.executor, which will generalize across the implementations of Executor I'm interested in? self can be &Self or &mut Self as needed here.

I've run into this before but I don't remember the exact way I solved it

If you're constructing the type that holds the executor, you could hold an "executor provider" that can handle the reborrowing internally

trait ExecutorProvider {
    type Executor: Executor;

    fn executor(&mut self) -> Self::Executor;
}

Alternatively you might be able to do something clever with BorrowMut as a bound?

2 Likes

I'm sorry I don't understand your question. Maybe you can post the struct and the relevant methods.

Ideally, sqlx would provide an implementation of Executor<'a> for &'a mut E where E:Executor.

If you're ok excluding the &Pool option, you might be able to write something like this:

fn f<E>(exec:&mut E) where for<'a> &'a mut E: Executor<'a> {
    // ...
}

Thanks, this is exactly the kick in the brainstem I needed. I ended up with this:

pub trait WithExecutor<'e> {
    type Executor: Executor<'e, Database = sqlx::postgres::Postgres>;

    fn executor(&'e mut self) -> Self::Executor;
}

impl<'p> WithExecutor<'p> for Repository<sqlx::postgres::PgPool> {
    type Executor = &'p sqlx::postgres::PgPool;

    fn executor(&'p mut self) -> Self::Executor {
        &self.executor
    }
}

impl<'t, 'c> WithExecutor<'t> for Repository<sqlx::Transaction<'c, sqlx::Postgres>>
where
    'c: 't,
{
    type Executor = &'t mut sqlx::Transaction<'c, sqlx::Postgres>;

    fn executor(&'t mut self) -> Self::Executor {
        &mut self.executor
    }
}

impl<'e, T> Repository<T>
where
    Self: WithExecutor<'e>,
{
    pub async fn submit<R, N, O, I>(
        &'e mut self,
        submission: Submission<R, N, O, I>,
    ) -> Result<Filed<R, N, O, I>>
    where
        R: Submit + Unpin + Send,
        N: Serialize + DeserializeOwned,
        O: Serialize + DeserializeOwned,
        I: Serialize + DeserializeOwned,
    {
        let submission = submission.into_storage()?;
        let result = R::submit(submission).fetch_one(self.executor()).await?; // <-- the troublesome line from my original post
        let result = result.into_app()?;

        Ok(result)
    }
}

It has some ergonomics issues, but nothing insurmountable. Thanks a ton for reminding me that this is an option.

1 Like

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.