Reputation: 10025
I need to generalize functions to pass an Executor
trait for SQLX code. In the code below with a concrete &mut SqliteConnection
parameter, main can call process
, or it can call process_twice
which calls process
2x times. All sqlx functions require arg type E: Executor
.
I need to make my code generic so that conn: &mut SqliteConnection
arg is also written with some generic, but so i can use it more than once.
Inside Sqlx, multiple structs implement Executor trait on a mutable reference, e.g.
impl<'c> Executor<'c> for &'c mut SqliteConnection {...}
I was able to convert a SINGLE call (the fn process
), but not the fn process_twice
- because executor is not copyable.
async fn process<'a, E>(conn: E) -> anyhow::Result<()>
where E: sqlx::Executor<'a, Database = sqlx::Sqlite> {...}
// [dependencies]
// anyhow = "1.0"
// futures = "0.3"
// sqlx = { version = "0.6", features = [ "sqlite", "runtime-tokio-native-tls"] }
// tokio = { version = "1.28.2", features = ["macros"] }
//
// //// TO RUN, must set env var:
// DATABASE_URL='sqlite::memory:' cargo run
use futures::TryStreamExt;
use sqlx::SqliteConnection;
use sqlx::{query, Connection};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut conn = SqliteConnection::connect("sqlite::memory:").await?;
process(&mut conn).await?;
process_twice(&mut conn).await?;
Ok(())
}
async fn process(conn: &mut SqliteConnection) -> anyhow::Result<()> {
let sql = query!("SELECT name FROM sqlite_master");
let mut rows = sql.fetch(conn);
while let Some(row) = rows.try_next().await? {
println!("{row:?}")
}
Ok(())
}
async fn process_twice(conn: &mut SqliteConnection) -> anyhow::Result<()> {
process(conn).await?;
process(conn).await?;
Ok(())
}
Similar questions: this
Upvotes: 0
Views: 1136
Reputation: 27539
The trick is to not parametrize the whole E
but just the type behind the reference:
async fn process_twice<T>(conn: &mut T) -> anyhow::Result<()>
where
for<'e> &'e mut T: Executor<'e, Database = Sqlite>,
{
process(&mut *conn).await?;
process(conn).await
}
That way you can still reborrow the reference. That does mean that you can't take Pool<DB>
any more because it only implements Executor
for &Pool
but should work for your usecase.
Upvotes: 3