Skip to content

Rust 后端:状态管理

在前面的章节中,我们创建了执行简单任务的 Rust 命令:接收输入、处理输入并返回结果。这些命令是无状态的——它们在每次执行之间不会记住任何信息。然而,大多数真实世界的应用程序都需要记住一些事情。无论是用户的登录会话、数据库连接还是简单的计数器,你都需要一种维护状态的方式。

想象一下你的 Tauri 命令是餐厅里的一位服务员。在无状态模型中,每次服务员走到你的桌边时,他们都完全忘记了你是谁以及你之前点了什么。为了获得连贯的用餐体验,服务员需要一个记事本记录信息以便稍后查阅。在 Tauri 中,那个记事本就是 tauri::State。

Rust 函数天生是独立的。为了在主进程(应用程序启动的地方)和你的命令处理程序(前端请求时运行)之间共享数据,Tauri 提供了一个托管状态容器系统。

首先,我们需要定义我们的状态是什么样子。在 Rust 中,我们使用 struct。在本教程中,我们将构建一个简单的计数器,即使前端重新加载也能保持其状态。

因为 Tauri 命令是异步的,并且可以同时从多个线程调用,我们不能简单地使用可变变量。我们需要将数据包装在线程安全的锁定机制中。最常见的标准方法是 Mutex (互斥锁)。

src-tauri/src/lib.rs
use std::sync::Mutex;
// 我们将整数封装在 Mutex 中,以允许安全的并发访问
struct AppState {
counter: Mutex<i32>,
}

一旦结构体定义完毕,我们必须告诉 Tauri 来“管理”它。这发生在应用程序通过 Builder 进行设置期间。当你调用 .manage() 时,Tauri 会将此实例注入其内部状态注册表,使其可供任何命令访问。

src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
// 初始化状态,计数器从 0 开始
.manage(AppState {
counter: Mutex::new(0)
})
.invoke_handler(tauri::generate_handler![increment_counter])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

现在 Tauri 正在管理我们的状态,我们如何取回它呢?我们在命令函数中使用 tauri::State 守卫作为参数。Tauri 的依赖注入系统会自动检测此参数并传入正确的状态实例。

要修改数据,我们必须首先 lock (锁定) Mutex。这确保了在任何给定时间只有一个命令可以修改计数器,从而防止竞态条件。

src-tauri/src/lib.rs
use tauri::State;
#[tauri::command]
fn increment_counter(state: State<AppState>) -> i32 {
// 锁定互斥锁以获得对内部整数的可变访问
// 这里使用 .unwrap() 来处理互斥锁“中毒”的罕见情况
let mut count = state.counter.lock().unwrap();
// 修改值
*count += 1;
// 将新值返回给前端
*count
}

在前端,这看起来与任何其他命令调用完全相同。JavaScript 不需要了解 Mutex 或 State。它只是简单地要求后端递增并显示结果。

// Frontend JavaScript/TypeScript
import { invoke } from '@tauri-apps/api/core';
async function addToCount() {
const newCount = await invoke('increment_counter');
console.log("Current count from Rust:", newCount);
}

通过这种设置,你可以重新加载网页(Ctrl+R 或 Cmd+R),计数器也不会重置为零。状态存在于 Rust 后端进程中,该进程独立于 webview 而保持活跃。