使用unwrap
在运行时很容易崩溃:
fn main() {
c().unwrap();
}
fn c() -> Option<i64> {
None
}
结果:
Compiling playground v0.0.1 (file:///playground)
Running `target/debug/playground`
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', ../src/libcore/option.rs:325
note: Run with `RUST_BACKTRACE=1` for a backtrace.
error: Process didn't exit successfully: `target/debug/playground` (exit code: 101)
unwrap
仅用于快速测试和概念验证吗?
我不能肯定“我的程序不会在这里崩溃,所以我可以使用unwrap
”如果我真的想在运行时避免使用panic!
,我认为避免使用panic!
就是我们想要的生产应用程序。
换句话说,如果我使用unwrap
,我可以说我的程序是可靠的吗?或者我必须避免使用unwrap
即使案件看似简单吗?
我读了this答案:
当您确定没有错误时,最好使用它。
但我认为我不能“肯定”。
我不认为这是一个意见问题,而是一个关于Rust核心和编程的问题。
虽然整个“错误处理” - 主题非常复杂并且通常基于意见,但这个问题实际上可以在这里得到解答,因为Rust有相当狭隘的哲学。那是:
panic!
编程错误(“错误”)Result<T, E>
和Option<T>
处理预期和可恢复的错误人们可以认为unwrap()
是在这两种错误之间进行转换(它将可恢复的错误转换为panic!()
)。当你在程序中编写unwrap()
时,你会说:
此时,
None
/Err(_)
值是编程错误,程序无法从中恢复。
例如,假设您正在使用HashMap
并希望插入一个您可能希望稍后变异的值:
age_map.insert("peter", 21);
// ...
if /* some condition */ {
*age_map.get_mut("peter").unwrap() += 1;
}
这里我们使用unwrap()
,因为我们可以确定该键具有值。如果它没有,甚至更重要,那将是一个编程错误:它不是真正可以恢复的。在那个时候你会做什么,关键"peter"
没有价值?再试一次插入......?
但是你可能知道,Rust的标准库里有一张漂亮的entry
API地图。使用该API,您可以避免所有这些unwrap()
s。这几乎适用于所有情况:您可以经常重构您的代码以避免unwrap()
!只有极少数情况下才有办法解决。但是如果你想发出信号就可以使用它:在这一点上,这将是一个编程错误。
最近有一篇关于“错误处理”主题的相当受欢迎的博客文章,其结论类似于Rust的哲学。这是相当长的但值得一读:“The Error Model”。以下是我总结与此问题相关的文章:
总结:当您确定所获得的可恢复错误实际上在此时无法恢复时,请使用unwrap()
。在受影响的线上方的评论中解释“为什么?”的奖励积分;-)
换句话说,如果我使用unwrap,我可以说我的程序是可靠的吗?或者即使案件看起来很简单,我也必须避免打开包裹吗?
我认为明智地使用unwrap
是你必须要学会处理的东西,它不能只是避免。
我的修辞问题是:
(1)就像解包,索引恐慌,如果你违反合同并尝试索引越界。这将是程序中的一个错误,但它没有像调用unwrap
那样受到关注。
(2)如果除数为零,就像解包,整数除法恐慌。
(3)与unwrap不同,add不会检查发布版本中的溢出,因此它可能会静默导致环绕和逻辑错误。
当然,有一些策略可以处理所有这些,而不会在代码中留下恐慌的情况,但是许多程序只是简单地使用例如边界检查。
这里有两个问题:
panic!
的用途unwrap
的用途panic!
是Rust中用于表示不可恢复情况/违反假设的工具。它可以用于崩溃一个程序,该程序在面对此故障时不可能继续(例如,OOM情况),或者解决编译器知道它无法执行(此时)。
unwrap
是一种便利,最好在生产中避免使用。关于unwrap
的问题在于它没有说明哪个假设被违反了,而是更好地使用功能相同的expect("")
,但也会给出错误的线索(不打开源代码)。
unwrap()
不一定是危险的。就像unreachable!()
一样,有些情况下你可以确定某些情况不会被触发。
返回Option
或Result
的函数有时只适用于更广泛的条件,但由于程序的结构,这些情况可能永远不会发生。
例如:当你从Vec
tor创建一个迭代器时,你知道它的确切长度,并且可以确定在它上面调用next()
多久会返回一个Some<T>
(你可以安全地unwrap()
它)。
展开非常适合原型制作,但不适合生产。完成初始设计后,请返回并用unwrap()
替换Result<Value, ErrorType>
。