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

问题描述:

这很容易在运行时用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核心和编程的问题。

+1

* Crash *是一个可怕的滥用词现今国际海事组织;人们甚至在有例外的语言中将它用于未处理的异常。例如,对程序的恐慌远没有segfault那么糟糕。恐慌是故意说“程序的状态是*错误*,我现在就停下来,没有两种方式。 – Shepmaster

+0

@Shepmaster好吧,谢谢你的这些精度! –

虽然整个“错误处理” - 主题非常复杂且通常以意见为基础,但这个问题实际上可以在这里得到解答,因为Rust的理念相当狭隘。那就是:

  • panic!编程错误(“错误”)
  • 正确的错误传播,并与Result<T, E>处理和Option<T>预期和可恢复错误

我们可以把unwrap() as 转换这两种错误之间(它我s将可恢复的错误转换为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()。这适用于几乎所有情况:您可以经常重构您的代码以避免unwrap()!只有在极少数情况下,它才是无法解决的。但是如果你想发出信号就可以使用它:在这一点上,这将是一个编程错误。


已经有上的“错误处理”,其结论是类似铁锈的哲学话题最近,相当受欢迎的博客文章。这是相当长的,但值得一读:“The Error Model”。这里是我的总结文章中关于这个问题的尝试:

  • 的编程错误恢复的错误。
  • 之间刻意区分使用“快速失败”编程错误的做法

总之:使用unwrap()当你确定可恢复的错误,你得到的其实是在这一点上不可恢复的。受影响的线路;-)

+0

轰隆,超级清晰!我仔细阅读并了解了很多!我会牢记这一点:“你可以经常重构代码以避免解包()!” –

+0

如果它只提到一个'expect()',我会给它+1。 – mcarton

+0

我宁愿他用'expect(“”)'来表示他在字符串中所期望的内容,而不是'unwrap()',说明上面评论中期望的内容! –

unwrap()不一定危险。与unreachable!()一样,有些情况下您可以确定某些情况不会被触发。

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

例如:当您从Vec创建一个迭代符你打造专业化自己,你知道它的确切长度,可以肯定调用next()多久就返回一个Some<T>(你可以安全地unwrap()它)。

+1

我个人认为'unwrap()'是危险的,因为'unwrap()'取决于之前编写的代码,所以编辑代码时需要非常小心,另外,你可能知道一些'unwrap()'可能是安全的,但是你*我认为如果你已经写了'unwrap()',最好是发表评论'//safe'在这些行中,我这样做是为了检查我使用'grep'打开多少个解包。 –

+0

这些条件在将来可能会或可能不会改变。我不是说这是一条规则 - 这完全可能。 – ljedrz

换句话说以上评论解释“为什么?”加分,我可以说,我的计划是可靠的,如果我使用展开?或者即使案件看起来很简单,我是否也必须避免拆包?

我认为使用unwrap明智地是你必须学会​​处理,它不能被避免。

我反问攻势将是:

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

(1)就像拆开包装,索引恐慌,如果你犯了一个违反合同并尝试为出界。这将是该程序中的一个错误,但它不像调用unwrap那样引起太多关注。 (2)如果除数为零,则类似于展开,整数除法恐慌。 (3)与unwrap不同,加法不检查发布版本中的溢出,因此它可能会静默地导致环绕和逻辑错误。

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

有折叠成一个这里有两个问题:

  • 是在生产过程中使用的panic!接受
  • 在生产

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

unwrap是一种方便,在生产中最好避免。关于unwrap的问题是,它没有说明哪个假设被违反,最好使用expect(""),它在功能上是等价的,但也会提供关于哪里出错的线索(无需打开源代码)。