在Rust中,何时应该使用unwrap而不是expect?

39

我是 Rust 的新手,正试图理解何时应该使用 unwrap 和 expect。

以下是一段示例代码:

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    let query = args.get(1).unwrap();
    println!("query from unwrawp: {}", query);

    let query = args.get(1).expect("insufficient arguments");
    println!("query from expect: {}", query);

    //$ cargo run hello
    //OUTPUT:
    //query from expect: hello
    //query from unwrawp: hello

}

我观察到的唯一区别是expect中有一个自定义的panic消息。
这两者是否可以互换或者有任何特殊情况需要使用其中一个而不是另一个?


4
据我所知,它们是可以互换使用的。一般情况下,如果逻辑非常明确不会出现错误,我会使用“unwrap”;如果存在未考虑到的边缘情况或者可能由于开发人员编写的配置文件不正确等触发的情况,则会使用带有消息的“expect”。请注意,这里只进行翻译,不提供解释和其他内容。 - notquiteamonad
1
这个 回答 提供了一个很好的概述,让你在选择 unwrap()expect() 时考虑错误模式。简而言之,unwrap() 是在可恢复错误和不可恢复错误之间转换的一种方式。相反地,在调用 expect() 时,你假设已经处于不可恢复的错误状态。我想指导原则应该是:如果你无法从 unwrap() 调用失败中恢复,请选择 expect() 并包含更好的错误信息。 - IInspectable
@IInspectable:我认为你误读了你链接的答案。unwrapexpect都将可恢复的转换为不可恢复的,唯一的区别在于一个指定了自定义错误消息,而另一个没有。 - Matthieu M.
1
@mat:我想我表达不清楚了。当没有东西可unwrap时,unwrap()expect()都会导致panic!()。在这方面,它们是相同的。而你的代码所处的情况则是有所不同的。如果你的代码不能在没有unwrap值的情况下继续运行,那么选择expect()。如果你可以响应没有值的情况,请选择unwrap(),但要先验证该值是否存在。在这种情况下,只有当你的代码出现问题从而从可恢复状态转换为不可恢复状态时,unwrap()才会导致panic!() - IInspectable
1
这两个选项都引入了一个函数。唯一的区别在于它们的名称。但这并不改变对unwrap()和其他内容需要的假设。那个其他内容被命名为expect()。具体名称并不重要,重要的是具有不同的属性。这使您可以向代码读者传达不同的语义。 - IInspectable
显示剩余2条评论
2个回答

48
Rust没有函数重载,因此需要一种声明带有消息的"unwrap"的方法,即expect
  1. expect == 带有消息的unwrap
  2. expect_err == 带有消息的unwrap_err
关于"unwrap vs expect"的使用场景,Rust Book (Ch 9)说:

使用expect代替unwrap,并提供良好的错误消息可以传达您的意图,并使跟踪panic源更容易。 因为这个错误消息以我们指定的文本开头……所以找到代码中这个错误消息来自哪里会更容易。


5
除了函数重载之外,可选参数也可以实现相同的功能,而 Rust 也没有这个特性。 - BlackShift

19

亚历山大·法迪耶夫的回答 是正确的,不过在Rust Book(第9章)中还有一些更多的信息:

当你有其他逻辑确保 Result 将具有一个 Ok 值,但这个逻辑不是编译器所理解的时候,调用 unwrap 也是合适的。您仍然需要处理一个 Result 值:无论如何,您调用的任何操作仍然有可能失败,即使在您特定的情况下逻辑上不可能失败。如果您可以通过手动检查代码来确保永远不会出现 Err 变量,那么调用 unwrap 是完全可以接受的。以下是一个示例:

use std::net::IpAddr;

let home: IpAddr = "127.0.0.1".parse().unwrap();
我们正在通过解析硬编码的字符串来创建一个IpAddr实例。我们可以看到127.0.0.1是一个有效的IP地址,因此在这里使用unwrap是可接受的。然而,有一个硬编码的有效字符串并不会改变parse方法的返回类型:我们仍然得到一个Result值,并且编译器仍然会要求我们处理Result,就好像Err变体是一种可能性,因为编译器不够聪明,无法看到这个字符串总是一个有效的IP地址。如果IP地址字符串来自用户而不是硬编码到程序中并且因此具有失败的可能性,我们肯定要用更健壮的方式处理Result

因此,如果您知道错误永远不会发生,但编译器要求您处理错误,则unwrap比需要提供自定义和不必要的错误消息(如果使用expect)更加简洁和简短。


当你以这种方式使用unwrap时,有时读者可能不立即明白为什么解包是安全的。因此,你可以在期望消息中写下解包是安全的原因,而不是在注释中解释。这样,当程序发生panic时,不仅更容易找到panic的源头,还能理解导致panic发生的条件。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接