std::sync::OnceLock与once_cell::sync::Lazy返回类型差异致编译错误
最初使用once_cell::sync::Lazy实现全局数据库连接池:
static POOL: Lazy<Pool<AsyncMysqlConnection>> = Lazy::new(|| { let config = AsyncDieselConnectionManager::<diesel_async::AsyncMysqlConnection>::new( "mysql://user@127.0.0.1:3306/db", ); Pool::builder(config).build().unwrap() });
通过let mut conn = POOL.get().await?;获取连接完全正常。但因需要动态传入数据库URL,改用std::sync::OnceLock:
pub static POOL: OnceLock<Pool<AsyncMysqlConnection>> = OnceLock::new();
并编写初始化函数:
pub fn set_pool(db_url: &str) { let pool = AsyncDieselConnectionManager::<AsyncMysqlConnection>::new(db_url); let conn = Pool::builder(pool).build().unwrap(); POOL.set(conn); }
调用POOL.get()获取值并使用时,出现编译错误:
the trait bound
deadpool::managed::Pool<AsyncDieselConnectionManager<AsyncMysqlConnection>>: DerefMutis not satisfied
--> src/diesel.rs:512:18
|
... .execute(&mut conn) ^^^^^^^ unsatisfied trait bound = help: the trait
DerefMutis not implemented forPool<AsyncDieselConnectionManager<...>>
= help: the following other types implement traitAsyncConnection:
AsyncMysqlConnection
AsyncPgConnection
SyncConnectionWrapper
= note: required forPool<AsyncDieselConnectionManager<...>>to implementAsyncConnection
= note: the full name for the type has been written to '/data/data/com.termux/files/home/lucle/target/debug/deps/lucle-aa4e36718ce9a19c.long-type-4366048800889042929.txt'
= note: consider using--verboseto print the full type name to the console
疑惑两者返回类型看似相同,为何会出现该错误?
核心问题是对两种全局变量的调用逻辑混淆:
- 用
Lazy时,POOL通过Dereftrait直接指向内部的Pool实例,所以POOL.get().await?实际是调用Pool的get方法,最终拿到的是AsyncMysqlConnection类型的连接,符合execute方法的参数要求。 - 用
OnceLock时,POOL.get()返回的是Option<&Pool<AsyncMysqlConnection>>——也就是连接池的引用,而非连接本身。如果直接把这个值当成连接传入execute,相当于把Pool实例当连接用,而Pool并没有实现AsyncConnectiontrait,自然会触发编译错误。
修正步骤:
- 正确获取连接池并获取连接:先从
OnceLock中取出连接池实例,再调用其get方法获取可用连接:
// 先确保连接池已初始化 let pool = POOL.get().ok_or_else(|| anyhow::anyhow!("数据库连接池未初始化"))?; // 再从连接池中获取连接 let mut conn = pool.get().await?; // 正常使用连接执行操作 conn.execute(...).await?;
- 修正初始化函数的变量命名:初始化函数里把
Pool实例命名为conn容易造成混淆,建议改成pool,同时添加初始化失败的断言:
pub fn set_pool(db_url: &str) { let manager = AsyncDieselConnectionManager::<AsyncMysqlConnection>::new(db_url); let pool = Pool::builder(manager).build().unwrap(); // 确保连接池只初始化一次 POOL.set(pool).expect("数据库连接池已被初始化"); }
内容的提问来源于stack exchange,提问作者Vana

