Rust Axum跨请求传递状态编译异常问题排查
编译失败原因及修复方案
核心问题
你的代码编译失败的根本原因是:持有标准库std::sync::Mutex的锁(MutexGuard)时执行异步await操作,导致返回的Future不满足Send约束。
标准库的MutexGuard没有实现Send trait,它被设计用于同步代码场景,不能跨线程传递。而Axum的Handler要求处理函数返回的Future必须是Send的(因为Axum默认在多线程运行时调度任务)。当你在持有MutexGuard的代码块里调用.await(比如数据库更新的UserBmc::update().await),异步任务的状态会包含这个非Send的MutexGuard,导致整个Future不满足Send,触发编译错误。
而单独执行读取验证码或数据库更新时不会报错:
- 单独读取验证码:锁在同步代码块内就释放了,
Future中不包含MutexGuard,满足Send; - 单独更新数据库:没有持有
MutexGuard,Future自然满足Send。
修复方案
有两种可行的修复方式,根据你的需求选择:
方案1:先释放锁,再执行异步操作
调整代码逻辑,先从锁中取出验证码,立刻释放锁,再执行数据库更新的异步操作:
pub async fn verify_check( ctx: CtxW, manager: &Manager, params: ParamsUpdateData<u32>, ) -> Result<User> { let ParamsUpdateData { data } = params; let code = data; let verify_store = Arc::clone(&manager.verify_store); let username = ctx.username.clone(); let user_id = ctx.user_id.clone(); // 同步块内获取验证码,离开块后自动释放锁 let stored_code = { let guard = verify_store.try_lock().map_err(|_| WebError::VerificationProblem)?; guard.get(&username).copied().ok_or(WebError::VerificationProblem)? }; if stored_code == code { println!("Its a match"); let data = UserUpdate { auth_level: None, verified: Some(true), }; let user = UserBmc::update::<UserUpdate, User>(&ctx.into(), &manager.mm, user_id, &data).await?; Ok(user) } else { Err(WebError::VerificationProblem) } }
方案2:使用异步锁替换标准库锁
将std::sync::Mutex替换为Tokio提供的tokio::sync::Mutex,它的MutexGuard实现了Send trait,可以安全地跨越await点:
- 首先修改
Manager的定义:
use tokio::sync::Mutex; use std::collections::HashMap; use std::sync::Arc; struct Manager { // 保留其他字段... verify_store: Arc<Mutex<HashMap<String, u32>>>, }
- 更新验证码存储函数
verify_request:
pub async fn verify_request(ctx: CtxW, manager: &Manager) -> Result<String> { let verify_store = manager.verify_store.clone(); let username = ctx.username.clone(); let rng = rand::rng().random_range(0..=10000); // 使用异步lock,需要await let mut guard = verify_store.lock().await.map_err(|_| WebError::VerificationProblem)?; guard.insert(username, rng); Ok("Verify request sent to mc server".to_string()) }
- 更新验证码校验函数
verify_check:
pub async fn verify_check( ctx: CtxW, manager: &Manager, params: ParamsUpdateData<u32>, ) -> Result<User> { let ParamsUpdateData { data } = params; let code = data; let verify_store = Arc::clone(&manager.verify_store); let username = ctx.username.clone(); let user_id = ctx.user_id.clone(); // 使用异步try_lock,需要await let guard = verify_store.try_lock().await.map_err(|_| WebError::VerificationProblem)?; let stored_code = guard.get(&username).copied().ok_or(WebError::VerificationProblem)?; if stored_code == code { println!("Its a match"); let data = UserUpdate { auth_level: None, verified: Some(true), }; let user = UserBmc::update::<UserUpdate, User>(&ctx.into(), &manager.mm, user_id, &data).await?; Ok(user) } else { Err(WebError::VerificationProblem) } }
内容的提问来源于stack exchange,提问作者Tez TenTen
相关产品推荐
相关产品推荐

