Rust Result 与 ?:把错误摆到明面上
更新时间:2026-09-20。Rust 没有 try/catch 异常,可恢复错误一律用
Result值返回,编译器强制你处理。不可恢复的程序错误才走 panic(见 panic 与 unwrap/expect)。
一、Result 与 Option 的分工
| 成功/有值 | 失败/无值 | 语义 | |
|---|---|---|---|
Result<T,E> | Ok(T) | Err(E) | 带原因的失败(IO 错误、解析失败) |
Option<T> | Some(T) | None | 单纯「可能没有」,无错误原因 |
Option/Result 的枚举与 match 基础见枚举、模式匹配与 Option/Result。
二、最朴素的处理:match 分流
rust
use std::fs;
let content = match fs::read_to_string("config.toml") {
Ok(s) => s,
Err(e) => {
eprintln!("读配置失败: {e},使用默认值");
String::from("default = true")
}
};match 保证 Ok/Err 两支都写,漏处理编译不过——这是 Rust 错误不会被静默吞掉的根本原因。
三、?:出错就提前返回
rust
use std::fs;
use std::io;
fn read_num(path: &str) -> io::Result<i32> {
let s = fs::read_to_string(path)?; // 失败立即 return Err
let n: i32 = s.trim().parse().map_err(io::Error::other)?;
Ok(n)
}expr? 等价于:成功取出 Ok 里的值继续;失败则提前 return Err(From::from(e))。因此它只能用在返回 Result(或 Option)的函数里。错误类型不同时靠 From 自动转换(见 From/Into 转换),不能转就用 map_err 手动映射。
四、组合子与 main 返回 Result
rust
// 不想写 match 时的链式组合
let n: i32 = "42".parse().unwrap_or(0); // 失败给默认值
let doubled = "7".parse::<i32>().map(|x| x * 2); // Ok(14)
// main 也可以返回 Result,Err 自动 Debug 打印、退出码非 0
fn main() -> anyhow::Result<()> {
let cfg = std::fs::read_to_string("c.toml")?;
println!("{cfg}");
Ok(())
}五、错误类型选型:thiserror vs anyhow
| 场景 | 选择 | 原因 |
|---|---|---|
| 库对外暴露的错误枚举 | thiserror | 派生 Error/Display,调用方能按变体 match |
| 应用顶层统一错误 | anyhow::Result | 任意错误都能 ? 进去,配 .context(...) 加上下文 |
| 只用标准库 | 手写 enum + impl std::error::Error | 零依赖 |
rust
use thiserror::Error;
#[derive(Error, Debug)]
enum AppError {
#[error("配置缺少字段 {0}")]
MissingField(String),
#[error(transparent)]
Io(#[from] std::io::Error), // 自动 From,? 直接转
}六、常见坑
- 在返回
()的函数里用?:编译错误「?operator can only be used in a function that returns Result/Option」,改函数签名或先match。 - 错误类型不匹配:函数内
?多种错误,需统一 enum(#[from])或上层用anyhow。 - 该用 Result 的地方用了 Option:解析、IO 失败有原因,用
None会丢掉排查线索。 - 库函数直接返回
anyhow::Error:调用方无法按变体匹配,库应暴露具名错误类型。 - 滥用
unwrap:只在「逻辑上绝不可能错」或原型里用,线上用?/默认值兜底。
总结
Rust 把可恢复错误设计成必须显式处理的 Result<T,E>:Ok 携值、Err 携原因,match 强制两支全覆盖;? 是「成功取值、失败提前 return Err 并经 From 自动转错误类型」的语法糖,只能在返回 Result/Option 的函数里用,类型不一致用 map_err 映射;库用 thiserror 暴露可匹配的具名错误枚举、应用顶层用 anyhow 统一并加 context,绝不要用 panic/unwrap 处理本可恢复的外部失败。