2017-01-07 39 views
8

我將定義值。但是這個值可能是哈希鍵的值。如果此鍵不存在,我將使用救援來定義值爲零。 例如爲什麼我們應該避免在其修飾形式中使用救援?

foo = bar[:a][:b][:c] rescue nil

但在實踐中,因爲我在修改的形式使用救援告訴我不好的風格。我將改變邏輯使用檢查三個條件。

foo = bar[:a][:b][:c] if bar.key?(:a) && bar[:a].key?(:b) && bar[:a][:b].key?(:c)

我真的很想知道爲什麼我們要避免在軌其修正形式使用救援?

+1

值得注意的是,雖然它是不好的風格,但它有合法的用途。 –

回答

14

爲什麼我們要避免在軌其修正形式使用救援?

首先,因爲它隱藏所有錯誤,包括你期望的人,而你沒有的那些,和毯子rescue不說清楚你的代碼錯誤是未來的讀者預期或意外。這可能不是一個問題現在,用一個簡單的foo[:a][:b][:c],但在一次有人任意點可能會修改語句讀取​​突然任何錯誤應該泡出來的some_method也被吞噬。其次,通常有一個更好的全面解決方案,它更明確地設計爲只處理您打算忽略的錯誤:缺少索引或返回值。

在你的情況下,替代是而不是你建議大量的if && && &&。對於一個哈希,你可以使用dig,其中有rescue所有好處,不用吞嚥每一類型的異常可能被上升:

foo = bar.dig(:a, :b, :c) 

同樣,對於鏈接的方法調用,您可以使用try(Rails中)或航行安全操作(在Ruby中2.3):

foo = bar.try(:a).try(:b).try(:c) 
# or 
foo = bar&.a&.b&.c 
+0

恕我直言,值得注意的是,引發異常是一個非常昂貴的操作。在不存在密鑰的情況下,通過使用其中一種方法返回'nil'通常會更快。 – spickermann

0

一個典型的例子,爲什麼它是一個糟糕的主意:

foo = ban[:a][:b][:c] rescue nil 

你可能會失去大量的時間檢查bar真的有{a: {b: {c: :something}}},不知道爲什麼foonil:有一個錯字,並rescue nil隱藏它。

替代品,看到@ meagar的很好的答案。

1

避免

... rescue ... 

出於同樣的原因爲

begin 
    ... 
rescue 
    ... 
end 

既不指定異常類,並因此默默地跳過所有的錯誤,而不是隻是你期望的人。在救助錯誤時,應始終儘可能具體。

另外,創建異常的成本很高。

當你引發一個異常時,回溯被填充,而在Rails中,這意味着創建500到1000個文件名和行號的字符串,因爲Rails往往有深度調用堆棧。所以如果你把你的rescue nil放在一個循環中,它最終可能會創建出成千上萬個從不使用的字符串對象,而對於Ruby的糟糕的垃圾收集來說,這會影響你的性能。

因此如果可能的話儘量使用不引發異常

foo = bar.dig(:a, :b, :c) 
3

替代更長的形式是比較安全的,除非沒有其他辦法,我也不會在生產中使用的短版。 您是否使用支票或豁免來避免或捕捉錯誤取決於具體情況。在處理器時間內,執行代價昂貴,因此,耗時的方法的基準測試,另一方面做了大量的檢查,但仍然不確定可能更糟糕。 如果我的代碼的可讀性會因爲進行大量檢查而丟失,而且速度不是我的一個因素,請使用begin .. rescue或def .. rescue,但是在這種情況下,您最好解決這樣的已知異常

begin 
    # - 
    raise "another exception" 
rescue SyntaxError 
    # - 
rescue => exception 
    @errors += 1 
    log exception 
    log exception.backtrace 
end 

其中給出

another exception 
C:/.../test.rb:3:in `<main>' 

總是趕那種異常,並把或更好的記錄它,我還用它來提高其記錄爲我所有的腳本,並通過獨立的監測變量@errors工具。

相關問題