2016-09-13 149 views
8

這很容易在運行時用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好吧,謝謝你的這些精度! –

回答

17

雖然整個「錯誤處理」 - 主題非常複雜且通常以意見爲基礎,但這個問題實際上可以在這裏得到解答,因爲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()',說明上面評論中期望的內容! –

1

unwrap()不一定危險。與unreachable!()一樣,有些情況下您可以確定某些情況不會被觸發。

函數返回OptionResult有時僅適用於更廣泛的條件,但由於您的程序結構如何,這些情況可能永遠不會發生。

例如:當您從Vec創建一個迭代符你打造專業化自己,你知道它的確切長度,可以肯定調用next()多久就返回一個Some<T>(你可以安全地unwrap()它)。

+1

我個人認爲'unwrap()'是危險的,因爲'unwrap()'取決於之前編寫的代碼,所以編輯代碼時需要非常小心,另外,你可能知道一些'unwrap()'可能是安全的,但是你*我認爲如果你已經寫了'unwrap()',最好是發表評論'//safe'在這些行中,我這樣做是爲了檢查我使用'grep'打開多少個解包。 –

+0

這些條件在將來可能會或可能不會改變。我不是說這是一條規則 - 這完全可能。 – ljedrz

4

換句話說以上評論解釋「爲什麼?」加分,我可以說,我的計劃是可靠的,如果我使用展開?或者即使案件看起來很簡單,我是否也必須避免拆包?

我認爲使用unwrap明智地是你必須學會​​處理,它不能被避免。

我反問攻勢將是:

  1. 我可以說我的程序是可靠的,如果我使用的載體,數組或切片索引?
  2. 如果我使用整數除法,我可以說我的程序是可靠的嗎?
  3. 如果我添加數字,我可以說我的程序是可靠的嗎?

(1)就像拆開包裝,索引恐慌,如果你犯了一個違反合同並嘗試爲出界。這將是該程序中的一個錯誤,但它不像調用unwrap那樣引起太多關注。 (2)如果除數爲零,則類似於展開,整數除法恐慌。 (3)與unwrap不同,加法不檢查發佈版本中的溢出,因此它可能會靜默地導致環繞和邏輯錯誤。

當然,還有一些策略可以處理所有這些,而不會在代碼中留下恐慌的情況,但許多程序僅僅用於例如邊界檢查。

2

有摺疊成一個這裏有兩個問題:

  • 是在生產過程中使用的panic!接受
  • 在生產

panic!使用unwrap接受的是一種工具,是在Rust中用於表示無法恢復的情況/違反的假設。它可以用於崩潰一個程序,這個程序在面對這種故障時不可能繼續(例如,OOM的情況),或者在知道它不能執行的時候解決編譯器(目前)。

unwrap是一種方便,在生產中最好避免。關於unwrap的問題是,它沒有說明哪個假設被違反,最好使用expect(""),它在功能上是等價的,但也會提供關於哪裏出錯的線索(無需打開源代碼)。

相關問題