如何将sqlx::Executor传递给嵌套Future?
解决PgExecutor多次传递时的移动语义问题(兼容PgPool与PgTransaction)
核心问题是PgExecutor的所有权转移导致二次调用失败:当你把db作为值传递给inner函数时,所有权会被转移,后续无法再使用原变量;而PgConnection(事务解引用后的底层类型)不实现Clone,没法直接复制一份来规避这个问题。
以下是两种简洁的解决方案:
方案1:传递PgExecutor的引用给inner函数
修改inner函数的参数为引用类型,这样不会转移所有权,outer中的db可以被多次复用,同时兼容PgPool和PgTransaction场景:
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<()> { // 传递db的引用,不转移所有权 dbg!(inner(&db, "first").await?); 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 }
原理说明
sqlx的fetch_one等查询方法支持接受引用类型的Executor,&PgPool、&mut PgTransaction都实现了PgExecutor trait,因此传递引用完全符合查询要求,同时避免了所有权转移的问题。
方案2:将outer的参数改为引用类型(可选)
如果业务场景允许,也可以直接让outer函数接受引用类型,内部传递时更直观:
async fn outer(db: &impl PgExecutor<'_>) -> sqlx::Result<()> { dbg!(inner(db, "first").await?); 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 }
这种方式和方案1本质一致,只是把引用的层级提前到了outer的参数上,可根据代码结构选择使用。
内容的提问来源于stack exchange,提问作者imbolc
相关产品推荐
相关产品推荐

