Rust从入门到实战系列八十六:失败时 panic 的简写:unwrap 和 expect

match 能够胜任它的工作,不过它可能有点冗长并且不总是能很好的表明其意图。Result<T, E> 类型定义了很多辅助方法来处理各种情况。如果 Result 是成员 Err,unwrap 会为我们调用 panic!。这里是一个实践 unwrap 的例子:

fn main() {
let f = File::open("hello.txt").unwrap();
}

如果调用这段代码时不存在 hello.txt 文件,我们将会看到一个 unwrap 调用 panic! 时提供的错误信息:

```thread ‘main’ panicked at ‘called Result::unwrap() on an Err value: Error {
repr: Os { code: 2, message: “No such file or directory” } }’,
src/libcore/result.rs:906:4

还有另一个类似于 unwrap 的方法它还允许我们选择 panic! 的错误信息:expect。使用 expect 而不是unwrap 并提供一个好的错误信息可以表明你的意图并更易于追踪 panic 的根源。expect 的语法看起来像这样:
```use std::fs::File;
fn main() {
let f = File::open("hello.txt").expect("Failed to open hello.txt");
}

expect 与 unwrap 的使用方式一样:返回文件句柄或调用 panic! 宏。expect 在调用 panic! 时使用的错误信息将是我们传递给 expect 的参数,而不像 unwrap 那样使用默认的 panic! 信息。它看起来像这样:

2, message: "No such file or directory" } }', src/libcore/result.rs:906:4

因为这个错误信息以我们指定的文本开始,Failed to open hello.txt,将会更容易找到代码中的错误信息来自何处。如果在多处使用 unwrap,则需要花更多的时间来分析到底是哪一个 unwrap 造成了panic,因为所有的 unwrap 调用都打印相同的信息。
传播错误
当编写一个其实现会调用一些可能会失败的操作的函数时,除了在这个函数中处理错误外,还可以选择让调用者知道这个错误并决定该如何处理。这被称为 传播(propagating)错误,这样能更好的控制代码调用,因为比起你代码所拥有的上下文,调用者可能拥有更多信息或逻辑来决定应该如何处理错误。
如果文件不存在或不能读取,这个函数会将这些错误返回给调用它的代码:

use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}

首先让我们看看函数的返回值:Result<String, io :: Error>。这意味着函数返回一个 Result<T, E> 类型的值,其中泛型参数 T 的具体类型是 String,而 E 的具体类型是 io Error。如果这个函数没有出任何错误成功返回,函数的调用者会收到一个包含 String 的 Ok 值 —— 函数从文件中读取到的用户名。如果函数遇到任何错误,函数的调用者会收到一个 Err 值,它储存了一个包含更多这个问题相关信息的 io :: Error实例。这里选择 io :: Error 作为函数的返回值是因为它正好是函数体中那两个可能会失败的操作的错误返回值:File :: open 函数和 read_to_string 方法。
函数体以 File :: open 函数开头。接着使用 match 处理返回值 Result,类似于示例 9-4 中的 match,如果 File :: open 成功了,模式中变量 file 中的文件句柄会变成可变变量 f 的值同时函数继续。Err 的情况下,不再调用 panic!,而是使用 return 关键字提早返回整个函数并将 File :: open 返回的模式中变量 e中的错误值作为函数的错误返回值传递给调用者。
所以 f 中有了一个文件句柄,函数接着在变量 s 中创建了一个新 String 并调用文件句柄 f 的 read_to_string方法来将文件的内容读取到 s 中。read_to_string 方法也返回一个 Result 因为它也可能会失败:哪怕是File :: open 已经成功了。所以我们需要另一个 match 来处理这个 Result:如果read_to_string 成功了,那么这个函数就成功了,并返回文件中的用户名,它现在位于被封装进 Ok 的 s 中。如果read_to_string失败了,则像之前处理 File :: open 的返回值的 match 那样返回错误值。不过并不需要显式的调用return,因为这是函数的最后一个表达式。
调用这个函数的代码最终会得到一个包含用户名的 Ok 值,或者一个包含 io :: Error 的 Err 值。我们无从得知调用者会如何处理这些值。例如,如果他们得到了一个 Err 值,他们可能会选择 panic! 并使程序崩溃、使用一个默认的用户名或者从文件之外的地方寻找用户名。我们没有足够的信息知晓调用者具体会如何尝试,所以将所有的成功或失败信息向上传播,让他们选择合适的处理方法。
这种传播错误的模式在 Rust 是如此的常见,以至于 Rust 提供了 ? 问号运算符来使其更易于处理。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值