
各位 Rust 爱好者们,大家好。
今天我们看点轻松的内容------理解 Rust 的 Result 类型。
一起学习一下,如何使用 Result 表示计算成功或失败,以及如何用它提供的方法处理和转换其中的值。
定义
Result 是 Rust 标准库提供的泛型枚举,用来表示一次计算的结果:成功时是 Ok 变体,失败时是 Err 变体。
Result 的定义如下:
rust
enum Result<T, E> {
Ok(T),
Err(E),
}
类型参数 T 和 E 分别表示成功值和错误值的类型。也就是说,Result<T, E> 可以表示这样一次计算:成功时返回 T 类型的值,失败时返回 E 类型的错误。
使用示例
下面用 Result 表示一个可能失败的函数的返回结果:
rust
fn parse_int(s: &str) -> Result<i32, std::num::ParseIntError> {
s.parse::<i32>().map_err(|e| e.into())
}
fn main() {
let result = parse_int("5");
match result {
Ok(n) => println!("Parsed integer: {}", n),
Err(e) => println!("Error parsing integer: {}", e),
}
}
这个例子中的 parse_int 函数会尝试把字符串解析为整数。解析成功时,返回包含整数的 Ok 变体;解析失败时,返回包含相应 ParseIntError 的 Err 变体。
处理结果
可以用模式匹配处理 Result。上面的例子通过 match 表达式分别处理 Ok 和 Err 变体。也可以用 unwrap 方法取出 Ok 中的值,但如果 Result 是 Err,就会触发 panic:
rust
let result = parse_int("5");
let n = result.unwrap(); // Will panic if result is an Err variant
println!("Parsed integer: {}", n);
如果错误可能正常发生,生产代码中应显式处理或传播错误,避免直接调用 unwrap()。如果能够确定结果一定是 Ok,使用 unwrap() 也可以,但应说明这一判断的依据。
如果需要转换 Result 中的值,可以使用 map 和 map_err。
map 只转换 Ok 中的值,保留 Err 不变;map_err 只转换 Err 中的值,保留 Ok 不变。它们返回的仍然是 Result,不会自动处理或消除错误。
例如,下面的 divide 函数对两个整数做除法,并用 Result 表示除法是否成功:
rust
fn divide(x: i32, y: i32) -> Result<i32, DivisionError> {
if y == 0 {
return Err(DivisionError::DivideByZero);
}
Ok(x / y)
}
注:这里省略了
DivisionError的定义,后面的代码假定它包含DivideByZero和Negative两个变体。不过,当前函数只检查除数是否为零,不会因为负数返回Negative。另外,i32::MIN / -1会触发溢出 panic,因此这个函数并未把所有失败情况都转换成Err。
map 和 map_err
可以用 map 把除法成功后得到的值转换为其他类型,比如浮点数。这里转换的是整数除法的结果,不会恢复整数除法舍弃的小数部分:
rust
let result = divide(10, 2);
let f: Result<f32, DivisionError> = result.map(|n| n as f32);
也可以用 map_err 把错误值转换为其他类型:
rust
let result = divide(10, 0);
let f: Result<i32, &str> = result.map_err(|e| match e {
DivisionError::DivideByZero => "Divide by zero",
DivisionError::Negative => "Cannot divide by negative number",
});
更多处理 Result 的方法
除了 map 和 map_err,Result 还提供了其他处理和转换值的方法。来看几个例子:
rust
fn add_one(x: i32) -> Result<i32, &'static str> {
if x > 100 {
return Err("Number too large");
}
Ok(x + 1)
}
fn add_two(x: i32) -> Result<i32, &'static str> {
if x > 50 {
return Err("Number too large");
}
Ok(x + 2)
}
fn add_three(x: i32) -> Result<i32, &'static str> {
if x > 30 {
return Err("Number too large");
}
Ok(x + 3)
}
// Using and_then
let result = add_one(5).and_then(|x| add_two(x)).and_then(|x| add_three(x));
assert_eq!(result, Ok(11));
// Using or_else
let result = add_one(105).or_else(|e| add_two(5)).or_else(|e| add_three(5));
assert_eq!(result, Ok(7));
// Using unwrap_or
let result = add_one(105).unwrap_or(100);
assert_eq!(result, 100);
// Using unwrap_or_else
let result = add_one(105).unwrap_or_else(|e| -1);
assert_eq!(result, -1);
这里的三个函数各自执行一次运算,并返回 Result。and_then 可以把这些函数串起来。如果所有运算都成功,就返回最后一次运算的结果;任何一步失败,链条就会短路,返回对应的 Err,后续闭包不会执行。
or_else 与 and_then 类似,不过它处理的是 Err 变体。原来的计算失败时,可以通过它指定一次备用计算;如果已经成功,备用计算的闭包就不会执行。
unwrap_or 会返回 Ok 中的值;如果是 Err,则返回传入的默认值。当你希望出错时使用默认值,而不需要显式处理错误时,这个方法就能派上用场。
unwrap_or_else 与 unwrap_or 类似,不过它接收一个闭包。遇到 Err 时,闭包会接收其中的错误值,并计算出默认值。需要根据具体错误决定默认值时,可以用这个方法。
希望这些例子能帮你理解 Rust 中处理和转换 Result 的几种方法,以及它们各自的作用。