You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:33:20