外观
雷区 6:Result 嵌套地狱
症状
rust
use std::fs;
fn load_step1() -> Result<String, String> {
let content = fs::read_to_string("step1.txt").map_err(|e| format!("第一步失败:{}", e))?;
Ok(content)
}
fn load_step2(step1: &str) -> Result<String, String> {
let content = fs::read_to_string(&format!("{}.txt", step1.trim()))
.map_err(|e| format!("第二步失败:{}", e))?;
Ok(content)
}
fn main() {
let step1 = match load_step1() {
Ok(value) => value,
Err(error) => {
println!("{}", error);
return;
}
};
let step2 = match load_step2(&step1) {
Ok(value) => value,
Err(error) => {
println!("{}", error);
return;
}
};
println!("配置加载成功:{}", step2);
}每多一个"可能出错"的步骤,就多一个 match ... return——三步、五步、十步之后,代码被 match 的长梯子淹没,真正的业务逻辑被挤到角落。
诊断
这不是"语法错误",是结构味道——错误处理写成了"金字塔",因为每步都选择"当场处理"。
真实项目里,大多数函数其实不需要自己决定错误怎么办:把错误往上抛,让最终决策者(main、界面层)统一处理,更清晰。Rust 的 ? 就是为这个设计的——但很多人学了 ? 只用在函数内部,一到"组合多个调用"的场景又退回了 match 长梯子。
解药
解药一:? 自动传播,错误集中处理——每个函数只管"这一步",错误统一抛给 main:
rust
use std::fs;
fn load_step1() -> Result<String, String> {
fs::read_to_string("step1.txt").map_err(|e| format!("第一步失败:{}", e))
}
fn load_step2(step1: &str) -> Result<String, String> {
fs::read_to_string(&format!("{}.txt", step1.trim()))
.map_err(|e| format!("第二步失败:{}", e))
}
fn main() -> Result<(), String> {
let step1 = load_step1()?;
let step2 = load_step2(&step1)?;
println!("配置加载成功:{}", step2);
Ok(())
}main 里两个 ?,长梯子消失,业务逻辑一眼看全。任何一步失败,程序带着明确的错误消息退出——错误处理从"每一步"收拢到"出口"。
解药二:链式调用 + ?(一步到底)——能写成一行的,别拆开:
rust
fn main() -> Result<(), String> {
let step1 = fs::read_to_string("step1.txt").map_err(|e| e.to_string())?;
let step2 = fs::read_to_string(&format!("{}.txt", step1.trim()))
.map_err(|e| e.to_string())?;
println!("配置加载成功:{}", step2);
Ok(())
}解药三:真正的"嵌套决策"用 and_then / or_else——"第一步成功才做第二步"的语义:
rust
fn main() {
let result = load_step1()
.and_then(|step1| load_step2(&step1));
println!("{:?}", result);
}and_then:前一个 Ok 才继续,Err 直接短路(雷区 8 会细讲)。
预防
match处理错误不是罪,罪的是"每步都处理":函数内部处理一步、往外抛一步,才是正道- 闻到"金字塔"先重构:把
match换成?,把决策收拢到出口——这通常是安全的重构,测试还能帮你兜底 - ? 的代价:错误类型要统一(所以真实项目用 anyhow,雷区 7)
- 判断标准:这一步的错误,当前函数需要知道"是什么"吗? 不需要 →
?抛出去;需要 → 处理完再?或return