admin管理员组文章数量:1122846
I'm trying to make functions work with both PgPool
and PgTransaction
. The PgExecutor
seems to be meant for this. But I can't understand how to pass it around. It's implemented for PgPool
, which is Clone
and PgConnection
which isn't, so I can't just add the Clone
bound.
Here's a runnable example:
use sqlx::{PgExecutor, PgPool};
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = PgPool::connect("postgres:///").await?;
let mut tx = pool.begin().await?;
outer(&mut *tx).await?;
txmit().await
}
async fn outer(db: impl PgExecutor<'_>) -> sqlx::Result<()> {
dbg!(inner(db, "first").await?);
// The second invocation doesn't compile:
// use of moved value: `db` value used here after move
dbg!(inner(db, "second").await?);
Ok(())
}
async fn inner(db: impl PgExecutor<'_>, name: &str) -> sqlx::Result<String> {
sqlx::query_scalar!(r#"SELECT $1 as "name!""#, name)
.fetch_one(db)
.await
}
I'm trying to make functions work with both PgPool
and PgTransaction
. The PgExecutor
seems to be meant for this. But I can't understand how to pass it around. It's implemented for PgPool
, which is Clone
and PgConnection
which isn't, so I can't just add the Clone
bound.
Here's a runnable example:
use sqlx::{PgExecutor, PgPool};
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = PgPool::connect("postgres:///").await?;
let mut tx = pool.begin().await?;
outer(&mut *tx).await?;
tx.commit().await
}
async fn outer(db: impl PgExecutor<'_>) -> sqlx::Result<()> {
dbg!(inner(db, "first").await?);
// The second invocation doesn't compile:
// use of moved value: `db` value used here after move
dbg!(inner(db, "second").await?);
Ok(())
}
async fn inner(db: impl PgExecutor<'_>, name: &str) -> sqlx::Result<String> {
sqlx::query_scalar!(r#"SELECT $1 as "name!""#, name)
.fetch_one(db)
.await
}
Share
Improve this question
asked Nov 21, 2024 at 10:30
imbolcimbolc
1,8131 gold badge22 silver badges33 bronze badges
1
|
2 Answers
Reset to default 1While the impl
-trait type (impl PgExecutor<'_>
) is sufficient for executing queries, nothing about that type indicates to Rust that it may be reused or duplicated. One way to solve this is to specify the concrete type (PgConnection
) and use a mutable reference:
use sqlx::{PgConnection, PgPool};
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = PgPool::connect("postgres:///").await?;
let mut tx = pool.begin().await?;
outer(&mut *tx).await?;
tx.commit().await
}
async fn outer(db: &mut PgConnection) -> sqlx::Result<()> {
dbg!(inner(db, "first").await?);
dbg!(inner(db, "second").await?);
Ok(())
}
async fn inner(db: &mut PgConnection, name: &str) -> sqlx::Result<String> {
sqlx::query_scalar!(r#"SELECT $1 as "name!""#, name)
.fetch_one(db)
.await
}
The borrow checker is able to understand that, while &mut PgConnection
can't arbitrary be duplicated, it can be reused for a second call to inner
once the first call to inner
is done.
Jofas from the Rust Forum found the solution that allows both outer
and inner
be compatible with the PgPool
and PgConnection
:
use sqlx::{Acquire, PgExecutor, PgPool, Postgres};
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = PgPool::connect("postgres:///").await?;
let mut tx = pool.begin().await?;
outer(&mut *tx).await?;
tx.commit().await
}
async fn outer(db: impl Acquire<'_, Database = Postgres>) -> sqlx::Result<()> {
let mut connection = db.acquire().await?;
dbg!(inner(&mut *connection, "first").await?);
dbg!(inner(&mut *connection, "second").await?);
Ok(())
}
async fn inner(db: impl PgExecutor<'_>, name: &str) -> sqlx::Result<String> {
sqlx::query_scalar!(r#"SELECT $1 as "name!""#, name)
.fetch_one(db)
.await
}
Though in complicated scenarios, there's a lifetime issue with the Acquire approach, forcing you to resort to the Connection-based approach:
implementation of `sqlx::Acquire` is not general enough
= note: `sqlx::Acquire<'_>` would have to be implemented for the type `&mut PgConnection`
= note: ...but `sqlx::Acquire<'0>` is actually implemented for the type `&'0 mut PgConnection`, for some specific lifetime `'0`
本文标签: rustHow to pass sqlxExecutor to nested futuresStack Overflow
版权声明:本文标题:rust - How to pass `sqlx::Executor` to nested futures - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736311794a1934864.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
query_scalar!
toquery_scalar
is a step towards making it work without one. – Finn Bear Commented Nov 22, 2024 at 4:21