SQLx中&mut *transaction与&mut transaction的区别解析
Rust中
&mut *transaction与&mut transaction的区别解析 问题描述
我好奇示例代码里&mut *transaction与&mut transaction的区别,原以为transaction并非指针,但前者满足Executor trait约束,后者却不满足。如果把.execute(&mut *transaction)改为.execute(&mut transaction)会触发编译错误,想了解这是否属于类型转换,以及编译器区分二者是否合理。
可运行代码
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(()) } }
修改后触发的编译错误
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]
原因解析
1. 类型转换的本质:DerefMut特征的作用
sqlx::Transaction是一个封装了数据库连接和事务状态的包装类型,它实现了DerefMut特征——这个特征允许通过*操作符对实例进行解引用,获取它内部持有的PgConnection的可变引用。
&mut *transaction的执行逻辑:- 先通过
DerefMut对transaction解引用,得到&mut PgConnection类型的值 - 再对该值进行可变借用(这里是重新借用,最终类型仍为
&mut PgConnection)
- 先通过
&mut transaction的类型则是&mut Transaction<'_, Postgres>,这是包装类型本身的可变引用
2. Executor trait的约束匹配
从编译错误信息可以看到,sqlx只为&mut PgConnection、连接池引用等实际执行SQL的底层类型实现了Executor trait,并没有为&mut Transaction实现该trait。因此:
&mut *transaction转换后的&mut PgConnection满足Executor约束,可以正常调用execute方法&mut transaction的类型不满足约束,触发编译错误
3. 编译器区分二者的合理性
这完全符合Rust的静态类型设计原则:
- Rust严格区分不同类型,包装类型
Transaction和它内部的PgConnection是完全不同的类型 - 解引用操作不会自动隐式触发在函数参数匹配中,必须显式写出
&mut *transaction来告诉编译器需要通过DerefMut进行类型转换 - 这种设计保证了类型安全,避免了隐式转换可能带来的意外行为
内容的提问来源于stack exchange,提问作者financial_physician
相关产品推荐
相关产品推荐

