📝 本文首发于 栏轩·阁
欢迎访问阅读原文,获取更好的阅读体验。
Rust 没有 try-catch,没有异常。它的错误处理分两类:不可恢复 的直接崩(panic!),可恢复 的用 Result<T, E> 返回值,编译器强制你处理。这一篇把两种方式讲透。
1. panic!:不可恢复的错误
panic! 是 Rust 的「崩了」------遇到无法继续的情况,程序直接终止:
rust
let v = vec![1, 2, 3];
// let x = v[99]; // panic: index out of bounds
和 C 的段错误不同,panic! 会打印调用栈,告诉你崩在第几行。只在真正的 bug(数组越界、除零)时才 panic,业务错误别用。
2. Result<T, E>:可恢复的错误
文件不存在、网络超时、输入格式不对------这些不该崩,应该返回错误让调用方决定怎么办。Result 就是干这个的:
rust
enum Result<T, E> {
Ok(T), // 成功,带返回值
Err(E), // 失败,带错误信息
}
2.1 match 处理
rust
let f = File::open("hello.txt");
match f {
Ok(file) => println!("文件打开成功"),
Err(error) => println!("文件打开失败: {error}"),
}
编译器知道你收到了 Result,不处理两种情况之一就编译不过------和 Option 一样,编译期安全网。
2.2 快捷方法
rust
// unwrap:成功返回值,失败直接 panic
// let f = File::open("not_exist.txt").unwrap(); // panic!
// expect:和 unwrap 一样,但可以自定义 panic 信息
// let f = File::open("not_exist.txt").expect("必须存在这个文件");
// unwrap_or_else:失败时执行一个闭包
let f = File::open("not_exist.txt").unwrap_or_else(|err| {
println!("无法打开: {err}");
File::create("fallback.txt").unwrap() // 创建替代文件
});
unwrap 和 expect 适合原型阶段快速验证,生产代码建议用 match 或 ?。
3. ? 运算符:错误传播
多层函数调用时,每层都写 match 来处理错误再 return Err(...) 会很冗长。? 做了这件事------如果值是 Err,直接 return Err;如果是 Ok,就把里面的值取出来:
rust
fn read_file_content() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?; // 失败就直接 return Err
let mut content = String::new();
f.read_to_string(&mut content)?;
Ok(content)
}
没有 ? 的话要写成:
rust
let mut f = match File::open("hello.txt") {
Ok(file) => file,
Err(e) => return Err(e),
};
? 把它压缩成了 File::open("hello.txt")? 一行。链式调用更简洁:
rust
fn read_file_short() -> Result<String, io::Error> {
let mut content = String::new();
File::open("hello.txt")?.read_to_string(&mut content)?;
Ok(content)
}
关键:? 只能用在返回 Result 或 Option 的函数里。 在 main 函数或其他返回 () 的地方不能直接用 ?。
4. Option 与 Result 互转
Option 也可以配合 ?,但只能在返回 Option 的函数中:
rust
fn find_first_even(nums: &[i32]) -> Option<i32> {
for &n in nums {
if n % 2 == 0 {
return Some(n);
}
}
None
}
Option 转 Result------.ok_or():
rust
let result = find_first_even(&nums).ok_or("没找到偶数");
// Ok(8) 或 Err("没找到偶数")
Result 转 Option------.ok():
rust
let opt = File::open("hello.txt").ok(); // 成功变 Some(file),失败变 None
5. 链式组合
多个可能出错的操作用 ? 串起来,逻辑清晰:
rust
fn process_data() -> Result<i32, String> {
let nums = vec![10, 20, 30];
let first = nums.get(0).ok_or("列表为空")?;
let second = nums.get(1).ok_or("只有1个元素")?;
Ok(first + second)
}
每一步都可能失败,但代码读起来像成功路径一样流畅。这比嵌套 if-else 或 try-catch 块要舒服得多。
总结
| 机制 | 用途 | 编译期检查? |
|---|---|---|
panic! |
不可恢复的 bug | 否(运行时) |
Result |
可恢复的错误,调用方处理 | 是 |
Option |
值可能不存在 | 是 |
? |
简化 Result/Option 传播 |
是 |
核心思想:能用 Result/Option 就别用 panic! ,能用 ? 就别堆 match。
至此,Rust 基础语法的「必修课」已经全部覆盖。下一篇进入泛型与 Trait,开始写真正可复用的 Rust 代码。