Rust Rocket框架修改State报错E0596的解决方法及咨询
Rocket框架中State类型无法修改的解决方案
问题背景
使用Rocket 0.5.0-rc.3框架时,尝试通过State类型存储服务器状态并修改其中的数据,遇到以下编译错误:
error[E0596]: cannot borrow data in dereference of `rocket::State<FunnyDatabase>` as mutable --> src\routes.rs:9:25 | 9 | if let Some(room) = rooms.0.iter_mut().find(|room| room.name == room_id) { | ^^^^^^^^^^^^^^^^^^ cannot borrow as mutable | = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `rocket::State<FunnyDatabase>`
相关代码片段
路由函数:
#[post("/join_room/<room_id>/<player_name>")] pub fn join_room(room_id: String, player_name: String, rooms: &State<FunnyDatabase>) -> (Status, (ContentType, String)) { if let Some(room) = rooms.0.iter_mut().find(|room| room.name == room_id) { room.players.push_back(Player{ name: player_name.clone(), card: "UNKNOWN".to_string() }); } ...
模拟数据库结构体:
pub struct FunnyDatabase(pub LinkedList<Room>);
State初始化代码:
rocket::build() .manage(FunnyDatabase(LinkedList::<Room>::new())) ...
Room结构体定义:
pub struct Room { pub name: String, pub players: LinkedList<Player> }
问题原因
Rocket的State<T>提供的是不可变引用,设计目的是保证多线程/异步环境下的安全读取,因此不允许直接修改其内部数据。要实现可修改的全局状态,必须在FunnyDatabase内部封装线程安全的可变容器。
解决方案
方案1:使用std::sync::Mutex(同步场景)
适用于同步路由或对性能要求不高的场景:
- 修改
FunnyDatabase结构体,用Mutex包裹LinkedList:
use std::sync::Mutex; use std::collections::LinkedList; pub struct FunnyDatabase(pub Mutex<LinkedList<Room>>);
- 更新State初始化代码:
rocket::build() .manage(FunnyDatabase(Mutex::new(LinkedList::<Room>::new())))
- 修改路由函数,获取锁后再修改数据:
#[post("/join_room/<room_id>/<player_name>")] pub fn join_room(room_id: String, player_name: String, rooms: &State<FunnyDatabase>) -> (Status, (ContentType, String)) { // 获取Mutex锁,实际项目建议处理PoisonError(锁被panic线程持有) let mut rooms_list = rooms.0.lock().unwrap(); if let Some(room) = rooms_list.iter_mut().find(|room| room.name == room_id) { room.players.push_back(Player{ name: player_name.clone(), card: "UNKNOWN".to_string() }); } // 示例返回,根据实际需求调整 (Status::Ok, (ContentType::Json, "{\"status\": \"success\"}".to_string())) }
方案2:使用tokio::sync::RwLock(异步场景推荐)
Rocket 0.5默认是异步框架,使用tokio::sync::RwLock可以避免阻塞线程,提升并发性能:
- 修改
FunnyDatabase结构体:
use tokio::sync::RwLock; use std::collections::LinkedList; pub struct FunnyDatabase(pub RwLock<LinkedList<Room>>);
- 更新State初始化代码:
rocket::build() .manage(FunnyDatabase(RwLock::new(LinkedList::<Room>::new())))
- 修改路由函数为异步,并通过
await获取写锁:
#[post("/join_room/<room_id>/<player_name>")] pub async fn join_room(room_id: String, player_name: String, rooms: &State<FunnyDatabase>) -> (Status, (ContentType, String)) { // 获取写锁 let mut rooms_list = rooms.0.write().await; if let Some(room) = rooms_list.iter_mut().find(|room| room.name == room_id) { room.players.push_back(Player{ name: player_name.clone(), card: "UNKNOWN".to_string() }); } // 示例返回,根据实际需求调整 (Status::Ok, (ContentType::Json, "{\"status\": \"success\"}".to_string())) }
总结
Rocket的State本身不支持直接修改,必须通过内部封装线程安全的可变类型(如Mutex/RwLock)来实现全局状态的安全修改,适配同步或异步的业务场景。
内容的提问来源于stack exchange,提问作者Perwell
相关产品推荐
相关产品推荐

