人人都会AI编程

错误处理、异步命令、状态注入

更新时间:2026-07-11

错误处理:让前端看得懂的失败信息

Tauri 的 Rust 命令可以直接返回 Result<T, E>,框架会自动将 Ok 的结果序列化为 JavaScript 可用的值,并在 Err 时抛出异常。为了让前端 try-catch 能拿到结构化的错误信息,你的错误类型需要实现 Serialize。实践中常用 thiserror 派生 ErrorDisplay,再用 serde::Serialize 让它可序列化:

#[derive(Debug, thiserror::Error, serde::Serialize)]
enum AppError {
    #[error("File not found: {0}")]
    NotFound(String),
    #[error("Permission denied")]
    PermissionDenied,
}

#[tauri::command]
fn read_config(path: &str) -> Result<String, AppError> {
    std::fs::read_to_string(path).map_err(|_| AppError::NotFound(path.into()))
}

前端调用时,就像普通 API 一样用 try-catch 处理,错误对象会包含你定义的错误信息。这让你不必手工拼接错误码,也能让 UI 做精准的提示。


异步命令:不阻塞 UI 线程

凡是涉及文件 I/O、网络请求、复杂计算的 Rust 命令,都应该声明为异步函数。Tauri 内置了 tokio 运行时,你只需在函数前加 async,并在前端使用 invoke 返回的 Promise 等待结果,整个调用期间 WebView 界面不会被冻结:

#[tauri::command]
async fn fetch_data(url: &str) -> Result<String, AppError> {
    let resp = reqwest::get(url).await?;
    Ok(resp.text().await?)
}

注意:异步命令内部尽量避免使用同步阻塞操作(如 std::fs::read_to_string),应改用 tokio::fsspawn_blocking,否则会占用异步工作线程,导致其他命令被延迟。


状态注入:安全地共享数据库、配置等资源

像数据库连接池、应用配置、缓存这些需要在多个命令间共享的对象,可以通过 Tauri 的状态管理注入。使用 app.manage(...) 注入任意实现了 Send + Sync + 'static 的 struct,然后在命令参数中通过 State<T> 提取:

struct AppState {
    db_pool: sqlx::SqlitePool,
}

#[tauri::command]
async fn get_user(id: i32, state: tauri::State<'_, AppState>) -> Result<String, AppError> {
    let row = sqlx::query("SELECT name FROM users WHERE id = ?")
        .bind(id)
        .fetch_one(&state.db_pool)
        .await?;
    Ok(row.get::<String, _>("name"))
}

State 的获取是零成本抽象,不需要全局变量,也不会破坏 Rust 的所有权规则。这让你的代码更容易测试,也避免了手动管理全局锁的麻烦。


这三个特性组合在一起,基本覆盖了 Tauri 后端开发的大部分场景:你用异步保证 UI 流畅,用 Result 让前后端错误处理一致,用状态注入管理共享资源——最终得到的,是一个结构清晰、易于维护的桌面应用后端。