出错了怎么办:错误处理
原理
Rust 将错误分为两类:
panic! — 不可恢复,调用 std::panic! 宏触发栈展开(unwinding)或直接终止(abort)。展开时沿调用栈逐帧调用 drop,由 panic_handler 和 set_hook 控制。可在 Cargo.toml 中设置 panic = "abort" 跳过展开,减小二进制体积。
Result<T, E> — 可恢复,定义为带两个泛型参数的枚举:
enum Result<T, E> { Ok(T), Err(E) }? 运算符在编译时展开为 match + return Err,其中通过 From trait 做错误类型自动转换。每个 ? 相当于一个提前返回(early return),保证错误不丢失。
unwrap / expect 本质是 match result { Ok(v) => v, Err(e) => panic!(...)}。
Option<T> 与 Result<T, E> 的关系:None 可视为 Err(()) 但不携带额外信息。两者均可使用 ? 传播(需函数返回对应类型)。
语法
panic!
panic!("致命错误");
// 仅用于不可恢复的情况或原型阶段Result 基本使用
use std::fs::File;
let f = match File::open("data.txt") {
Ok(file) => file,
Err(e) => {
eprintln!("打开失败:{}", e);
return;
}
};unwrap / expect
let f = File::open("data.txt").unwrap(); // Err 时 panic
let f = File::open("data.txt").expect("必须存在"); // 自定义消息? 运算符 — 传播错误
use std::fs;
use std::io::{self, Read};
fn read_file() -> Result<String, io::Error> {
let mut f = File::open("data.txt")?; // Err 时提前返回
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let content = fs::read_to_string("data.txt")?;
println!("{}", content);
Ok(())
}错误类型匹配
use std::io::ErrorKind;
match File::open("data.txt") {
Ok(f) => f,
Err(e) => match e.kind() {
ErrorKind::NotFound => File::create("data.txt").unwrap(),
_ => panic!("{}", e),
},
};unwrap_or / unwrap_or_else
let x: Result<i32, &str> = Err("fail");
x.unwrap_or(0); // → 0
File::open("c.toml").unwrap_or_else(|e| {
if e.kind() == ErrorKind::NotFound {
File::create("c.toml").unwrap()
} else { panic!("{}", e); }
});错误处理策略
| 场景 | 方式 |
|---|---|
| 原型/示例 | unwrap() / expect() |
| 库代码 | Result<T, E> + ? 传播 |
| 致命错误 | panic! |
| 应用层 | main 中用 match 最后处理 |
实践
力扣问题
力扣: 力扣数组计数题 — unwrap_or 处理缺失
let val = map.get(&num).copied().unwrap_or(0);AI 自检
?从Result返回Err时,错误类型如何自动转换?Fromtrait 的作用?panic = "abort"和默认展开模式的区别是什么?对二进制大小有什么影响?