我应该避免在生产应用程序中打开包装吗?

问题描述 投票:13回答:5

使用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核心和编程的问题。

error-handling rust
5个回答
25
投票

虽然整个“错误处理” - 主题非常复杂并且通常基于意见,但这个问题实际上可以在这里得到解答,因为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()。在受影响的线上方的评论中解释“为什么?”的奖励积分;-)


5
投票

换句话说,如果我使用unwrap,我可以说我的程序是可靠的吗?或者即使案件看起来很简单,我也必须避免打开包裹吗?

我认为明智地使用unwrap是你必须要学会处理的东西,它不能只是避免。

我的修辞问题是:

  1. 如果我在向量,数组或切片上使用索引,我可以说我的程序是可靠的吗?
  2. 如果我使用整数除法,我可以说我的程序是可靠的吗?
  3. 如果我添加数字,我可以说我的程序是可靠的吗?

(1)就像解包,索引恐慌,如果你违反合同并尝试索引越界。这将是程序中的一个错误,但它没有像调用unwrap那样受到关注。

(2)如果除数为零,就像解包,整数除法恐慌。

(3)与unwrap不同,add不会检查发布版本中的溢出,因此它可能会静默导致环绕和逻辑错误。

当然,有一些策略可以处理所有这些,而不会在代码中留下恐慌的情况,但是许多程序只是简单地使用例如边界检查。


3
投票

这里有两个问题:

  • 是生产中可接受的panic!的用途
  • 是生产中可接受的unwrap的用途

panic!是Rust中用于表示不可恢复情况/违反假设的工具。它可以用于崩溃一个程序,该程序在面对此故障时不可能继续(例如,OOM情况),或者解决编译器知道它无法执行(此时)。

unwrap是一种便利,最好在生产中避免使用。关于unwrap的问题在于它没有说明哪个假设被违反了,而是更好地使用功能相同的expect(""),但也会给出错误的线索(不打开源代码)。


2
投票

unwrap()不一定是危险的。就像unreachable!()一样,有些情况下你可以确定某些情况不会被触发。

返回OptionResult的函数有时只适用于更广泛的条件,但由于程序的结构,这些情况可能永远不会发生。

例如:当你从Vector创建一个迭代器时,你知道它的确切长度,并且可以确定在它上面调用next()多久会返回一个Some<T>(你可以安全地unwrap()它)。


-2
投票

展开非常适合原型制作,但不适合生产。完成初始设计后,请返回并用unwrap()替换Result<Value, ErrorType>

© www.soinside.com 2019 - 2024. All rights reserved.