处理混合错误中基本的方法就是将其互相嵌套。
use std::num::ParseIntError;
fn double_first(vec: Vec<&str>) -> Option<Result<i32, ParseIntError>> {
vec.first().map(|first| {
first.parse::<i32>().map(|n| 2 * n)
})
}
fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
println!("The first doubled is {:?}", double_first(numbers));
println!("The first doubled is {:?}", double_first(empty));
// Error 1: the input vector is empty
println!("The first doubled is {:?}", double_first(strings));
// Error 2: the element doesn't parse to a number
}
有时我们希望遇到错误时停止处理(例如使用?),但当Option为None时继续执行。这时transpose函数就能起到作用了,它可以交换Result和Option的位置。
use std::num::ParseIntError;
fn double_first(vec: Vec<&str>) -> Result<Option<i32>, ParseIntError> {
let opt = vec.first().map(|first| {
first.parse::<i32>().map(|n| 2 * n)
});
opt.transpose()
}
fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
println!("The first doubled is {:?}", double_first(numbers));
println!("The first doubled is {:?}", double_first(empty));
println!("The first doubled is {:?}", double_first(strings));
}
代码解析
问题背景
代码需要处理两种不同的“空”或“错误”情况:
- Option::None:表示值不存在(例如:从一个空向量中获取第一个元素)。这通常不被视为一个需要终止程序的严重错误,程序逻辑可以继续处理这种情况。
- Result::Err:表示操作失败(例如:将一个字符串解析为整数时失败)。这通常是一个需要传播的错误,我们希望立即停止当前操作并返回这个错误。
?操作符行为
?操作符对Result和Option的行为不同:
在Result上使用?:如果是Ok(value),则解包value;如果是Err(e),则立即从当前函数返回Err(e)。
在Option上使用?:如果是Some(value),则解包出value;如果是None,则立即从当前函数返回None。
在这个场景中,我们不希望遇到None时就立即返回(停止函数),而是希望将它转换为Ok(None)。直接使用?无法实现这个目标。
transpose方法的使用
这正是transpose方法的用武之地。它用于转换嵌套的容器类型。具体在这里:
- 转换前:opt的类型是Option<Result<i32,ParseIntError>>。这个类型意味着:我们有一个可能的值(Option),而这个值本身又是一个可能出错的操作结果(Result)。
- 转换后:opt.transpose()返回的类型是Result<Option<i32>,ParseIntError>。这个类型意味着:这个操作的结果可能是一个出错的结果(Result)。如果没有错误,这个值可能是Option<i32>。
transpose的转换规则:
如果opt是Some(Ok(value))->返回Ok(Some(value))
如果opt是Some(Err(e))->返回Err(e),遇到错误,立即停止
如果opt是None->返回Ok(none),遇到None,继续处理,将其包装成成功的结果。
这完美地实现了我们的需求:错误(Err)导致提前返回,而空值(None)则被保留并继续处理。
原文链接:https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/option_result.html