外观
雷区 1:unwrap 满天飞,一炸炸一片
症状
rust
fn main() {
let input = String::from("abc");
let number: u32 = input.parse().unwrap();
println!("{}", number);
}运行输出
text
thread 'main' panicked at src/main.rs:3:37:
called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace程序直接崩溃,只留下一句:called Result::unwrap() on an Err value。
诊断
unwrap() 是"我不处理错误,出错就炸"的简写——它是第 2 章 .expect() 的"不写遗言"版本。
unwrap 不是不能用,是不能乱用。 什么时候炸?任何 Err 出现就炸。而 Err 出现的原因往往是外部世界:用户输入、文件不存在、网络断了……这些恰恰是最不该炸的场合。真实项目的崩溃事故,一大半来自"这里不可能会出错吧"的 unwrap——结果它真的出错了,程序当场去世,用户一脸懵。
解药
解药一:该商量的地方,用 match/if let——输入、文件、网络这些"外部错误":
rust
fn main() {
let input = String::from("abc");
match input.parse::<u32>() {
Ok(number) => println!("{}", number),
Err(_) => println!("请输入数字哦。"),
}
}解药二:把错误传出去(?)——函数内部不决定,让调用者决定:
rust
fn read_number() -> Result<u32, String> {
let input = std::io::stdin().lines().next()...
.ok_or("没有输入")?;
input.parse().map_err(|_| "请输入数字".to_string())
}解药三:unwrap 的正确用法——只在"逻辑上不可能失败"的地方:
rust
let first = numbers.first().unwrap(); // 前面检查过 not empty,逻辑上一定有
let config = read_config().unwrap(); // 程序启动时配置缺失,炸了也算合理(启动失败)预防
- 判断标准:用户能撞上的,别 unwrap;程序员写错的,才配 unwrap(第 8 章哲学)
- unwrap 像"撞墙",expect 像"撞墙前喊一句"——真要 unwrap,优先 expect,把"为什么这里不可能失败"写进遗言
- clippy 会提醒你:Rust 的静态检查工具 clippy(
cargo clippy)会标注可避免的 unwrap——让工具帮你把关 - 临时代码可以 unwrap,提交前清理:写原型时 unwrap 飞快;上线前,把"外部错误"的 unwrap 全换成正式处理