楼层: 首页/ 软件技术/ Rust 语言基础/ 错误处理:panic、Result 与 ? 运算符
07

错误处理:panic、Result 与 ? 运算符

Error Handling · panic! · ? operator

Rust 把错误分成两类:不可恢复的用 panic!(直接崩溃),可恢复的用 Result<T, E>(返回给调用者处理)。没有受检异常那一套,全靠类型表达。

panic!:不可恢复错误

fn main() { // panic! 直接让程序崩溃退出 // panic!("出大事了"); // 默认会"栈展开",一层层退栈并 drop // 想看崩溃现场,设环境变量: // RUST_BACKTRACE=1 cargo run let v = vec![1,2,3]; println!("{}", v.get(10).unwrap_or(&-1)); // 越界不 panic,返回 None }

论什么时候 panic

库函数别随便 panic——把错误返回给调用者;二进制主程序在确实"没法继续"时 panic 也合理。Cargo.toml 里 panic = "abort" 可以让崩溃直接中止而不展开,省二进制体积(嵌入式/Release 常用)。

? 运算符:错误传播的语法糖

use std::fs::File; use std::io::{self, Read}; // 读文件全部内容:可能失败的地方,后面加一个 ? fn read_username() -> Result<String, io::Error> { let mut file = File::open("user.txt")?; // 失败就 return Err(...) 出去 let mut s = String::new(); file.read_to_string(&mut s)?; // 又一个 ? Ok(s) }

论? 到底干了啥

不用 ?,上面每个错误都得写一堆 match:match ... { Ok(v)=>v, Err(e)=>return Err(e) }。? 就是把这套样板自动帮你写了:是 Ok 就取里面的值继续,是 Err 就立刻 return。它还会自动用 From 把错误类型转成函数声明的错误类型,所以不同来源的错误能往一个 Result 里塞。? 在 Option 上也能用:let v = opt?; 是 None 就提前返回 None。

自定义错误类型与 thiserror / anyhow

// 手写:定义枚举错误 + impl Display + impl Error(啰嗦) #[derive(Debug)] enum MyError { NotFound, Io(io::Error), } // 生产里直接用 thiserror 派生,一行搞定(库 crate 用它) // [dependencies] // thiserror = "2" // anyhow = "1" /* #[derive(thiserror::Error, Debug)] enum MyErr { #[error("用户 {0} 没找到")] NotFound(String), #[error(transparent)] Io(#[from] std::io::Error), // #[from] 自动实现 From 转换 } */ fn main() { println!("库用 thiserror 定义精确错误类型;二进制用 anyhow::Result 灵活兜底"); }
thiserror vs anyhow 的分工
库定位用在哪
thiserror帮你派生精确的自定义错误类型写库时用——对外暴露清晰的错误变体,方便调用者 match
anyhow应用层灵活错误处理,anyhow::Result<T>,可加上下文 .context("...")写二进制应用时用——不用定义一堆错误类型,出问题打日志就够
生产环境别滥用 unwrap()

.unwrap() 遇到 Err/None 会直接 panic,把进程搞崩。示例代码里用用可以,生产代码要么 ? 传播、要么 match/map_err 妥善处理。.expect("为什么这里不该出错") 比 unwrap 多了一句说明,也是给 debug 看的,生产慎用。