Ryan Davis ' Ruby QuickRef sagt (ohne Erklärung):
Rettung der Ausnahme nicht. JE. oder ich werde dich erstechen.
Warum nicht? Was ist das Richtige?
ruby
exception-handling
John
quelle
quelle
Antworten:
TL; DR : Verwenden Sie
StandardError
stattdessen für das allgemeine Abfangen von Ausnahmen. Wenn die ursprüngliche Ausnahme erneut ausgelöst wird (z. B. beim Speichern, um nur die Ausnahme zu protokollieren),Exception
ist das Speichern wahrscheinlich in Ordnung.Exception
ist die Wurzel der Ausnahme Hierarchie Ruby so, wenn durescue Exception
dich von Rettung alles , einschließlich der Unterklassen wieSyntaxError
,LoadError
, undInterrupt
.Die Rettung
Interrupt
verhindert, dass der Benutzer CTRLCdas Programm beendet.Die Rettung
SignalException
verhindert, dass das Programm korrekt auf Signale reagiert. Es wird nicht tötbar sein, außer durchkill -9
.Rettung
SyntaxError
bedeutet, dasseval
s, die fehlschlagen, dies stillschweigend tun.All dies kann angezeigt werden, indem Sie dieses Programm ausführen und versuchen, es CTRLCoder
kill
es:Die Rettung von
Exception
ist nicht einmal die Standardeinstellung. TunException
rettet nicht vor , es rettet vorStandardError
. Sie sollten im Allgemeinen etwas Spezifischeres als die Standardeinstellung angebenStandardError
, aber die Rettung aus dem BereichException
erweitert den Bereich, anstatt ihn einzuschränken, und kann katastrophale Folgen haben und die Fehlersuche äußerst schwierig machen.Wenn Sie eine Situation haben, aus der
StandardError
Sie retten möchten, und Sie mit Ausnahme der Ausnahme eine Variable benötigen, können Sie dieses Formular verwenden:was äquivalent ist zu:
Einer der wenigen Fälle, in denen eine Rettung sinnvoll ist,
Exception
ist die Protokollierung / Berichterstellung. In diesem Fall sollten Sie die Ausnahme sofort erneut auslösen:quelle
Throwable
in Java zu fangenADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
und dannrescue *ADAPTER_ERRORS => e
Die eigentliche Regel lautet: Werfen Sie keine Ausnahmen weg. Die Objektivität des Autors Ihres Zitats ist fraglich, wie die Tatsache zeigt, dass es mit endet
Beachten Sie natürlich, dass Signale (standardmäßig) Ausnahmen auslösen und normalerweise lang laufende Prozesse über ein Signal beendet werden. Wenn Sie also Exception abfangen und nicht bei Signalausnahmen beenden, ist Ihr Programm sehr schwer zu stoppen. Also mach das nicht:
Nein, wirklich, tu es nicht. Führen Sie das nicht einmal aus, um zu sehen, ob es funktioniert.
Angenommen, Sie haben einen Thread-Server und möchten, dass alle Ausnahmen nicht:
thread.abort_on_exception = true
).Dann ist dies in Ihrem Verbindungsbehandlungs-Thread vollkommen akzeptabel:
Das Obige funktioniert mit einer Variation von Rubys Standard-Ausnahmebehandlungsroutine, mit dem Vorteil, dass Ihr Programm dadurch nicht ebenfalls beendet wird. Rails tut dies in seinem Request-Handler.
Signalausnahmen werden im Haupt-Thread ausgelöst. Hintergrund-Threads werden sie nicht bekommen, daher macht es keinen Sinn, sie dort zu fangen.
Dies ist besonders nützlich in einer Produktionsumgebung, wo Sie nicht stoppen Ihr Programm wollen einfach , wenn etwas schief geht. Dann können Sie die Stack-Dumps in Ihre Protokolle aufnehmen und zu Ihrem Code hinzufügen, um bestimmte Ausnahmen weiter unten in der Aufrufkette und auf elegantere Weise zu behandeln.
Beachten Sie auch, dass es eine andere Ruby-Sprache gibt, die den gleichen Effekt hat:
Wenn in dieser Zeile
do_something
eine Ausnahme ausgelöst wird, wird sie von Ruby abgefangen, weggeworfen unda
zugewiesen"something else"
.Tun Sie dies im Allgemeinen nicht, außer in besonderen Fällen, in denen Sie wissen, dass Sie sich keine Sorgen machen müssen. Ein Beispiel:
Die
debugger
Funktion ist eine gute Möglichkeit, einen Haltepunkt in Ihrem Code festzulegen. Wenn Sie jedoch außerhalb eines Debuggers und von Rails ausgeführt werden, wird eine Ausnahme ausgelöst. Theoretisch sollten Sie den Debugger-Code nicht in Ihrem Programm herumliegen lassen (pff! Niemand macht das!), Aber Sie möchten ihn möglicherweise aus irgendeinem Grund für eine Weile dort lassen, aber Ihren Debugger nicht kontinuierlich ausführen.Hinweis:
Wenn Sie ein anderes Programm ausgeführt haben, das Signalausnahmen abfängt und ignoriert (sagen Sie den obigen Code), dann:
pgrep ruby
oderps | grep ruby
suchen Sie sie und führen Sie sie auskill -9 <PID>
.Wenn Sie mit einem anderen Programm arbeiten, das aus irgendeinem Grund mit diesen Blöcken zum Ignorieren von Ausnahmen gespickt ist, ist es eine mögliche Ausrede, dies an die Spitze der Hauptzeile zu setzen:
Dies bewirkt, dass das Programm auf die normalen Beendigungssignale reagiert, indem es Ausnahmebehandler ohne Bereinigung sofort beendet und umgeht . Dies kann zu Datenverlust oder Ähnlichem führen. Achtung!
Wenn Sie dies tun müssen:
Sie können dies tatsächlich tun:
Im zweiten Fall
critical cleanup
wird jedes Mal aufgerufen, ob eine Ausnahme ausgelöst wird oder nicht.quelle
kill -9
.ensure
werden unabhängig davon ausgeführt, ob eine Ausnahme ausgelöst wurde oder nicht, während dierescue
Ausführung nur ausgeführt wird, wenn eine Ausnahme ausgelöst wurde.TL; DR
Nicht
rescue Exception => e
(und die Ausnahme nicht erneut auslösen) - oder Sie könnten von einer Brücke fahren.Angenommen, Sie sitzen in einem Auto (mit Ruby). Sie haben kürzlich ein neues Lenkrad mit dem drahtlosen Upgrade-System (das verwendet
eval
) installiert , aber Sie wussten nicht, dass einer der Programmierer die Syntax durcheinander gebracht hat.Sie befinden sich auf einer Brücke und stellen fest, dass Sie ein Stück in Richtung Geländer fahren. Biegen Sie also links ab.
Hoppla! Das ist wahrscheinlich Not Good ™, zum Glück erhöht Ruby a
SyntaxError
.Das Auto sollte sofort anhalten - richtig?
Nee.
Man merkt , etwas falsch ist , und Sie Slam auf die Notbremse (
^C
:Interrupt
)Ja - das hat nicht viel geholfen. Du bist ziemlich nah an der Schiene, also stellst du das Auto in den Park (
kill
ing :)SignalException
.In der letzten Sekunde ziehen Sie die Schlüssel (
kill -9
) heraus und das Auto hält an, Sie knallen nach vorne in das Lenkrad (der Airbag kann sich nicht aufblasen, weil Sie das Programm nicht ordnungsgemäß gestoppt haben - Sie haben es beendet) und den Computer auf der Rückseite Ihres Autos knallt auf den Sitz davor. Eine halb volle Dose Cola läuft über die Papiere. Die Lebensmittel auf der Rückseite sind zerkleinert und die meisten sind mit Eigelb und Milch bedeckt. Das Auto muss dringend repariert und gereinigt werden. (Datenverlust)Hoffentlich haben Sie eine Versicherung (Backups). Oh ja - weil sich der Airbag nicht aufgeblasen hat, sind Sie wahrscheinlich verletzt (gefeuert werden usw.).
Aber warte! Es gibt
MehrGründe, warum Sie verwenden möchtenrescue Exception => e
!Nehmen wir an, Sie sind dieses Auto und möchten sicherstellen, dass sich der Airbag aufbläst, wenn das Auto seinen sicheren Bremsimpuls überschreitet.
Hier ist die Ausnahme von der Regel: Sie können
Exception
nur fangen , wenn Sie die Ausnahme erneut auslösen . Eine bessere Regel ist es also, niemals zu schluckenException
und den Fehler immer wieder zu erhöhen.Das Hinzufügen von Rettung ist in einer Sprache wie Ruby leicht zu vergessen, und das Setzen einer Rettungserklärung direkt vor dem erneuten Ansprechen eines Problems fühlt sich ein wenig nicht trocken an. Und Sie wollen die Aussage nicht vergessen
raise
. Und wenn Sie dies tun, viel Glück beim Versuch, diesen Fehler zu finden.Zum Glück ist Ruby fantastisch. Sie können einfach das
ensure
Schlüsselwort verwenden, um sicherzustellen, dass der Code ausgeführt wird. Dasensure
Schlüsselwort führt den Code aus, egal was passiert - wenn eine Ausnahme ausgelöst wird, wenn dies nicht der Fall ist, ist die einzige Ausnahme, wenn die Welt endet (oder andere unwahrscheinliche Ereignisse).Boom! Und dieser Code sollte sowieso laufen. Der einzige Grund, den Sie verwenden sollten,
rescue Exception => e
ist, wenn Sie Zugriff auf die Ausnahme benötigen oder wenn nur Code für eine Ausnahme ausgeführt werden soll. Und denken Sie daran, den Fehler erneut auszulösen. Jedes Mal.Hinweis: Wie @Niall hervorhob, stellen Sie sicher, dass immer ausgeführt wird . Dies ist gut, da Ihr Programm Sie manchmal anlügen und keine Ausnahmen auslösen kann, selbst wenn Probleme auftreten. Bei kritischen Aufgaben wie dem Aufblasen von Airbags müssen Sie sicherstellen, dass dies auf jeden Fall geschieht. Aus diesem Grund ist es eine gute Idee, bei jedem Anhalten des Autos zu überprüfen, ob eine Ausnahme ausgelöst wird oder nicht. Obwohl das Aufblasen von Airbags in den meisten Programmierkontexten eine ungewöhnliche Aufgabe ist, ist dies bei den meisten Reinigungsaufgaben tatsächlich ziemlich häufig.
quelle
ensure
als Alternative zurescue Exception
ist irreführend - das Beispiel impliziert, dass sie gleichwertig sind, aber wie angegebenensure
wird es passieren, ob es eine Ausnahme gibt oder nicht. Jetzt werden Ihre Airbags aufgeblasen, weil Sie über 8 km / h gefahren sind, obwohl nichts schief gelaufen ist.Weil dies alle Ausnahmen erfasst. Es ist unwahrscheinlich, dass sich Ihr Programm von einem dieser Programme erholen kann.
Sie sollten nur Ausnahmen behandeln, von denen Sie wissen, wie Sie sie wiederherstellen können. Wenn Sie eine bestimmte Art von Ausnahme nicht erwarten, behandeln Sie sie nicht, stürzen Sie laut ab (schreiben Sie Details in das Protokoll), diagnostizieren Sie dann Protokolle und korrigieren Sie den Code.
Ausnahmen zu schlucken ist schlecht, tu das nicht.
quelle
Dies ist ein spezieller Fall der Regel, dass Sie keine Ausnahme abfangen sollten, mit der Sie nicht umgehen können. Wenn Sie nicht wissen, wie Sie damit umgehen sollen, ist es immer besser, einen anderen Teil des Systems abfangen und damit umgehen zu lassen.
quelle
Ich habe gerade einen großartigen Blog-Beitrag darüber auf Honeybadger.io gelesen :
Rubys Ausnahme gegen StandardError: Was ist der Unterschied?
quelle