有没有什么好的情况我们应该使用`unwrap`? [关闭]
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有没有什么好的情况我们应该使用`unwrap`? [关闭]相关的知识,希望对你有一定的参考价值。
由于使用unwrap
可能会出现问题,因为它在错误情况下崩溃,因此可能会被视为危险用法。
如果我百分之百确定它不会崩溃,如下列情况,该怎么办?
if option.is_some() {
let value = option.unwrap();
}
if result.is_ok() {
let result_value = result.unwrap();
}
由于我们已经检查过Result
和Option
,因此使用unwrap()
不会发生崩溃。但是,我们可以使用match
或if let
。在我看来,match
或if let
使用更优雅。
让我们关注Result
;我会在最后回到Option
。
Result
的目的是发出一个可能成功或失败并带有错误的结果。因此,任何使用它都应该属于这一类。让我们忽略一个箱子为不可能失败的操作返回Result
的情况。
通过做你正在做的事情(检查if result.is_ok()
然后提取值),你实际上做了两次相同的事情。第一次,你正在检查Result
的内容,第二次,你正在检查和提取不安全。
这确实可以用match
或map
完成,而且两者都比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;
}
}
但是,这种实现既笨重又低效。我们可以通过完全避免使用unwrap
和if
语句来简化这一过程。
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
类似,支持相同的操作。如果你想要处理Option
或Result
的内容并相应地进行分支,并且你正在使用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
很好。一旦我有点不确定,我就会诉诸if
,match
,map
和其他人。
请注意,还有expect
,它基本上是“在错误情况下打印注释的unwrap
”。但是,我发现这不符合人体工程学。而且,如果unwrap
失败,我发现回溯有点难以阅读。因此,我目前使用宏verify!
,其唯一参数是Option
或Result
,并检查该值是unwrap
able。它实现如下:
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`? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
最终一致性和实时一致性是什么?在架构设计中,我们应该选择哪种方式?