2017-01-07 7 views
8

Ich werde Wert definieren. Aber dieser Wert kann im Wert des Schlüssels des Hashes sein. Ich verwende Rettung für definieren Wert ist Null, wenn dieser Schlüssel nicht existiert. zum BeispielWarum sollten wir vermeiden, Rettung in seiner Modifikatorform zu verwenden?

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

Aber in der Praxis sagen Sie mir schlechten Stil, weil ich in seiner Modifikator Form mit Rettung. Ich werde die Logik ändern, um die Check-Drei-Bedingung zu verwenden.

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

Ich möchte wirklich wissen, warum sollten wir in Schienen mit Rettungs in seiner Modifikator Form vermeiden?

+1

Es ist erwähnenswert, dass es, obwohl es einen schlechten Stil ist, legitime Anwendungen dafür gibt. –

Antwort

14

Warum sollten wir vermeiden, Rettung in seiner Modifikationsform in Schienen zu verwenden?

Zum einen, weil es alle Fehler versteckt, einschließlich die, die Sie erwarten, und die, die Sie nicht, und eine Decke rescue es nicht klar, zukünftige Leser Ihres Codes macht die Fehler aufgetreten sind erwartet oder unerwartet. Dies könnte kein Problem jetzt, mit einer einfachen foo[:a][:b][:c] sein, aber zu einem bestimmten Zeitpunkt jemand könnte diese Aussage ändern foo[:a][:b][some_method] und plötzlich Fehler zu lesen, dass sollte Blase aus some_method ist auch geschluckt.

Zweitens gibt es normalerweise eine bessere, weniger allumfassende Lösung, die explizit entworfen wurde, um nur den Fehler zu behandeln, den Sie ignorieren möchten: Ein fehlender Index oder ein nil Rückgabewert.

In Ihrem Fall ist die Alternative nicht die massive if && && && Sie vorschlagen. Für einen Hash, können Sie entweder dig verwenden, die alle Vorteile der rescue hat, ohne jede Art von Ausnahme beim Schlucken, die ausgelöst werden könnte:

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

Auch für verkettete Methodenaufrufe können Sie try verwenden (in Rails) oder der sichere Navigation Operator (in Ruby 2.3):

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

IMHO ist es erwähnenswert, dass das Auslösen einer Ausnahme eine ziemlich teure Operation ist. Die Rückgabe von 'nil' durch Verwendung einer der Alternativen ist in Fällen, in denen ein Schlüssel nicht existiert, normalerweise viel schneller. – spickermann

0

ein typisches Beispiel dafür, warum es eine schlechte Idee ist:

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

Sie könnten viel Zeit verlieren zu überprüfen, dass bar wirklich hat {a: {b: {c: :something}}} und fragen, warum foo ist nil: Es gibt einen Tippfehler, und rescue nil verbirgt es.

Für Alternativen, siehe @ Meagar's gute Antwort.

1

Vermeiden

... rescue ... 

aus dem gleichen Grund wie

begin 
    ... 
rescue 
    ... 
end 

Weder gibt eine Ausnahmeklasse und überspringt somit geräuschlos und nicht alle Fehler als nur die eine, die Sie erwarten. Sie sollten immer so genau wie möglich sein, wenn Sie Fehler beheben.

Außerdem gibt es eine große Kosten für die Erstellung einer Ausnahme.

Wenn Sie eine Ausnahme auslösen, ist das Backtrace ausgefüllt, und in Rails bedeutet das Erstellen von 500 bis 1000 Strings mit Dateinamen und Zeilennummern, da Rails tendenziell einen tiefen Callstack hat. Also, wenn Sie Ihre rescue nil in eine Schleife setzen, kann es am Ende Tausende von String-Objekten, die nie verwendet werden, enden, und mit Rubys abgründigen Müllsammlung wird dies Ihre Leistung beeinflussen.

daher, wenn möglich versuchen, Alternativen zu verwenden, die nicht Ausnahme auslösen können

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

Je länger Form ist sicher, würde ich nicht die kurze Version in der Produktion verwenden, wenn es keine andere Möglichkeit gibt. Ob Sie Prüfungen oder eine Ausnahme verwenden, um Fehler zu vermeiden oder zu erfassen, hängt von der Situation ab. Ausnahmen sind teuer in der Prozessorzeit, so sind Ihre Benchmarks für zeitaufwendige Methoden, auf der anderen Seite eine Menge Prüfungen und noch nicht sicher ist, ist vielleicht noch schlimmer. Wenn die Lesbarkeit meines Codes durch eine Menge von Prüfungen verloren gehen würde und die Geschwindigkeit kein Faktor ist, benutze ich beginne .. Rettung oder def .. Rettung, aber in diesem Fall ist es besser, eine bekannte Ausnahme wie diese zu retten

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

welche

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

immer fangen die Art von Ausnahme und setzen oder besser zu protokollieren es gibt, ich benutze es auch eine Variable @errors zu erheben, die durch eine separate für alle meine Skripte und überwacht wird protokolliert Werkzeug.

Verwandte Themen