如何在Tauri后端全局存储Rust的Save结构体并供所有命令访问?
解决方案:在Tauri后端全局存储宝可梦存档结构体
要在Tauri后端全局维护Save实例,避免前后端频繁传递完整存档数据,推荐使用Tauri原生的State结合线程安全包装类型,这是最贴合Tauri生命周期的实现方式。以下是具体步骤:
1. 确保结构体线程安全
首先要保证Save及其内部嵌套结构体(Name、Pokemon)实现Send + Sync trait——Tauri的State要求存储的类型必须是线程安全的。如果你的结构体没有使用Rc、RefCell这类非线程安全的类型,默认会自动实现这两个trait;如果有,需要将其替换为Arc、Mutex等线程安全的替代方案。
2. 定义全局状态类型
创建一个包含线程安全包装的全局状态结构体,用Arc<Mutex<Option<Save>>>来存储可能未加载的存档:
use std::sync::{Arc, Mutex}; use tauri::State; // 全局应用状态,用于存储加载的存档 #[derive(Default)] struct AppState { save: Arc<Mutex<Option<PKRust::Save>>>, }
3. 注册全局状态到Tauri App
在main函数中,将AppState注册到Tauri应用的生命周期中:
fn main() { tauri::Builder::default() .manage(AppState::default()) // 注册全局状态 .invoke_handler(tauri::generate_handler![get_save_file, edit_trainer_money, save_to_file]) .run(tauri::generate_context!()) .expect("Failed to run Tauri app"); }
4. 修改命令逻辑,操作全局状态
加载存档并写入全局状态
修改get_save_file命令,将加载后的Save实例存入全局状态,同时完善错误处理:
#[tauri::command] async fn get_save_file(state: State<'_, AppState>) -> Result<String, String> { // 选择存档文件 let file_path = FileDialogBuilder::new() .add_filter("Save Files", &["sav"]) .pick_file() .map_err(|_| "Failed to select save file")?; // 加载存档 let save = PKRust::Save::load(&file_path) .map_err(|e| format!("Failed to load save: {}", e))?; // 将存档写入全局状态 *state.save.lock().map_err(|_| "Failed to access save state")? = Some(save.clone()); // 返回存档的字符串表示给前端(可选,用于展示基础信息) Ok(save.to_string()) }
编辑存档内容
添加修改存档字段的命令,直接操作全局状态中的Save实例:
#[tauri::command] async fn edit_trainer_money(state: State<'_, AppState>, new_money: u32) -> Result<(), String> { let mut save_guard = state.save.lock().map_err(|_| "Failed to lock save state")?; let save = save_guard.as_mut().ok_or("No save file loaded")?; // 修改金钱字段 save.money = new_money; Ok(()) }
保存存档到文件
添加保存命令,将全局状态中的Save实例写入文件:
#[tauri::command] async fn save_to_file(state: State<'_, AppState>, target_path: String) -> Result<(), String> { let save_guard = state.save.lock().map_err(|_| "Failed to lock save state")?; let save = save_guard.as_ref().ok_or("No save file loaded")?; // 将存档写入目标文件 save.save(&target_path) .map_err(|e| format!("Failed to save file: {}", e))?; Ok(()) }
替代方案:全局静态变量
如果不想用Tauri的State,也可以用lazy_static创建全局静态变量(同样需要线程安全包装):
use lazy_static::lazy_static; use std::sync::Mutex; lazy_static! { static ref GLOBAL_SAVE: Mutex<Option<PKRust::Save>> = Mutex::new(None); } // 加载存档命令示例 #[tauri::command] async fn get_save_file() -> Result<String, String> { let file_path = FileDialogBuilder::new() .add_filter("Save Files", &["sav"]) .pick_file() .map_err(|_| "Failed to select file")?; let save = PKRust::Save::load(&file_path)?; *GLOBAL_SAVE.lock().map_err(|_| "Lock poisoned")? = Some(save.clone()); Ok(save.to_string()) }
关键注意事项
- 必须用
Mutex(或RwLock)包装Save,保证多线程下的安全访问; - 避免使用
unwrap,改用map_err将错误转换为String返回给前端处理; - 如果
Save结构体需要频繁读写,推荐用RwLock替代Mutex,支持多线程读、单线程写的并发模式。
内容的提问来源于stack exchange,提问作者CatRass
相关产品推荐
相关产品推荐

