有没有什么好的情况我们应该使用`unwrap`? [关闭]

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有没有什么好的情况我们应该使用`unwrap`? [关闭]相关的知识,希望对你有一定的参考价值。

由于使用unwrap可能会出现问题,因为它在错误情况下崩溃,因此可能会被视为危险用法。

如果我百分之百确定它不会崩溃,如下列情况,该怎么办?

if option.is_some() {
    let value = option.unwrap();
}
if result.is_ok() {
    let result_value = result.unwrap();
}

由于我们已经检查过ResultOption,因此使用unwrap()不会发生崩溃。但是,我们可以使用matchif let。在我看来,matchif let使用更优雅。

答案

让我们关注Result;我会在最后回到Option

Result的目的是发出一个可能成功或失败并带有错误的结果。因此,任何使用它都应该属于这一类。让我们忽略一个箱子为不可能失败的操作返回Result的情况。

通过做你正在做的事情(检查if result.is_ok()然后提取值),你实际上做了两次相同的事情。第一次,你正在检查Result的内容,第二次,你正在检查和提取不安全。

这确实可以用matchmap完成,而且两者都比if更惯用。暂时考虑一下这个案例:

您有一个实现以下特征的对象:

use std::io::{Error, ErrorKind};

trait Worker {
    fn hours_left(&self) -> Result<u8, Error>;
    fn allocate_hours(&mut self, hours: u8) -> Result<u8, Error>;
}

我们假设hours_left()完全按照它在锡上所说的那样做。我们还假设我们有一个可变的借用Worker。让我们实施allocate_hours()

为了做到这一点,我们显然需要检查我们的工人是否还有额外的工作时间来分配。你可以写它类似于你的:

fn allocate_hours(&mut self, hours: u8) {
    let hours_left = self.hours_left();
    if (hours_left.is_ok()) {
        let remaining_hours = hours_left.unwrap();
        if (remaining_hours < hours) {
            return Err(Error::new(ErrorKind::NotFound, "Not enough hours left"));
        }
    // Do the actual operation and return
    } else {
        return hours_left;
    }
}

但是,这种实现既笨重又低效。我们可以通过完全避免使用unwrapif语句来简化这一过程。

fn allocate_hours(&mut self, hours: u8) -> Result<u8, Error> {
    self.hours_left()
        .and_then(|hours_left| {
            // We are certain that our worker is actually there to receive hours
            // but we are not sure if he has enough hours. Check.
            match hours_left {
                x if x >= hours => Ok(x),
                _ => Err(Error::new(ErrorKind::NotFound, "Not enough hours")),
            }
        })
        .map(|hours_left| {
            // At this point we are sure the worker has enough hours.
            // Do the operations
        })
}

我们在这里一举杀死了多只鸟。我们使代码更易读,更易于理解,并且我们已经删除了大量重复操作。这也开始看起来像Rust而不像php ;-)

Option类似,支持相同的操作。如果你想要处理OptionResult的内容并相应地进行分支,并且你正在使用unwrap,当你忘记解开某些东西时,你将不可避免地陷入许多陷阱。

有真正的案例,你的程序应该禁止。对于那些人,考虑expect(&str)而不是unwrap()

另一答案

在许多情况下,你可以通过更优雅的方式避免使用unwrap和其他人。但是,我认为有些情况下它是unwrap的正确解决方案。

例如,Iterator中的许多方法返回Option。让我们假设您有一个非空切片(已知由不变量非空)并且您想获得最大值,您可以执行以下操作:

assert!(!slice.empty()); // known to be nonempty by invariants
do_stuff_with_maximum(slice.iter().max().unwrap());

关于这一点可能有几种意见,但我认为在上述场景中使用unwrap完全没问题 - 在前面的assert!存在。

我的指导原则是:如果我正在处理的参数都来自我自己的代码,而不是与第三方代码接口,可能是assert!ing不变量,我对unwrap很好。一旦我有点不确定,我就会诉诸ifmatchmap和其他人。

请注意,还有expect,它基本上是“在错误情况下打印注释的unwrap”。但是,我发现这不符合人体工程学。而且,如果unwrap失败,我发现回溯有点难以阅读。因此,我目前使用宏verify!,其唯一参数是OptionResult,并检查该值是unwrapable。它实现如下:

pub trait TVerifiableByVerifyMacro {
    fn is_verify_true(&self) -> bool;
}
impl<T> TVerifiableByVerifyMacro for Option<T> {
    fn is_verify_true(&self) -> bool {
        self.is_some()
    }
}
impl<TOk, TErr> TVerifiableByVerifyMacro for Result<TOk, TErr> {
    fn is_verify_true(&self) -> bool {
        self.is_ok()
    }
}
macro_rules! verify {($e: expr) => {{
    let e = $e;
    assert!(e.is_verify_true(), "verify!({}): {:?}", stringify!($e), e)
    e
}}}

使用此宏,上述示例可以写为:

assert!(!slice.empty()); // known to be nonempty by invariants
do_stuff_with_maximum(verify!(slice.iter().max()).unwrap());

如果我不能unwrap的值,我收到一个提示slice.iter().max()的错误消息,以便我可以快速搜索我的代码库以查找发生错误的位置。 (根据我的经验,这比通过回溯查找错误的来源更快。)

以上是关于有没有什么好的情况我们应该使用`unwrap`? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

matlab中的unwrap有啥作用

oracle unwrap解密工具

matlab里的unwrap的命令用法是啥?

最终一致性和实时一致性是什么?在架构设计中,我们应该选择哪种方式?

Rust学习 - Result/Option/unwrap/?

Java单元测试对私有方法测试