rusttraitsrust-sqlx

What is the difference between `&mut *transaction` and `&mut transaction` in SQLx?


I'm curious what the difference is between &mut *transaction and &mut transaction in my example since I didn't think that transaction was a pointer to begin with but for some reason &mut *transaction satisfies the trait constraint while &mut transaction does not.

Here is my working code:

use sqlx::{PgPool, Transaction, Executor};
use chrono::{DateTime, Utc};

pub struct SimpleDbClient {
    db: PgPool,
}

pub trait AddCharacterEntries {
    async fn insert_character_entries(&self, timestamp: DateTime<Utc>, char_values: Vec<(char, i32)>) -> sqlx::Result<()>;
}

impl AddCharacterEntries for SimpleDbClient {
    async fn insert_character_entries(&self, timestamp: DateTime<Utc>, char_values: Vec<(char, i32)>) -> sqlx::Result<()> {
        let mut transaction = self.db.begin().await?;
        for (character, ascii_value) in char_values {
            sqlx::query!(
                r#"
                INSERT INTO character_log (timestamp, character, ascii_value)
                VALUES ($1, $2, $3)
                "#,
                timestamp,
                character as char,
                ascii_value
            )
            .execute(&mut *transaction)
            .await?;
        }
        transaction.commit().await?;
        Ok(())
    }
}

If I change the above to .execute(&mut transaction) I get this error:

rustc: the trait bound `&mut sqlx::Transaction<'_, sqlx::Postgres>: sqlx::Executor<'_>` is not satisfied
the following other types implement trait `sqlx::Executor<'c>`:
  <&'c mut sqlx::PgConnection as sqlx::Executor<'c>>
  <&'c mut sqlx::sqlx_postgres::PgListener as sqlx::Executor<'c>>
  <&'c mut sqlx::AnyConnection as sqlx::Executor<'c>>
  <&sqlx::Pool<DB> as sqlx::Executor<'p>> [E0277]
rustc: required by a bound introduced by this call [E0277]

Does this boil down to doing some sort of type casting? Curious if this would be considered a limitation or if it actually makes sense for the compiler to think these two expressions are different objects.


Solution

  • I didn't think that transaction was a pointer to begin with

    What you're missing is that Transaction implements Deref and DerefMut. Rust can auto-deref via these traits when you attempt to invoke a method on a value that implements Deref, but you can also explicitly deref using the * operator. &mut then takes a reference to this value, which is a PgConnection in this case.

    Because sqlx implements these traits, this allows you to invoke any (non-consuming) method of the underlying database connection on a Transaction value transparently, similar to how you can transparently execute any (non-consuming) methods of the slice type on a Vec.

    Note that reborrowing (&* and &mut *) are common patterns when working with types that implement Deref (and maybe DerefMut). Some examples: