Was ist der Unterschied zwischen dem Auslösen von Ausnahmen und dem Auslösen von Ausnahmen in Ruby?

167

Ruby hat zwei verschiedene Ausnahmemechanismen: Werfen / Fangen und Erhöhen / Retten.

Warum haben wir zwei?

Wann sollten Sie das eine und nicht das andere verwenden?

Nick Retallack
quelle
In vielen Programmiersprachen ist es üblich, aus verschachtelten Schleifen herauszukommen. Neben dem gotoin C / C ++, wie @docwhat erwähnt hat, hat Java break and continue gekennzeichnet . (Python hat auch einen abgelehnten Vorschlag dafür.)
Franklin Yu

Antworten:

104

Ich denke, http://hasno.info/ruby-gotchas-and-caveats hat eine anständige Erklärung für den Unterschied:

Fangen / Werfen ist nicht dasselbe wie Erhöhen / Retten. Mit catch / throw können Sie Blöcke schnell bis zu einem Punkt verlassen, an dem ein Fang für ein bestimmtes Symbol definiert ist. Raise Rescue ist die eigentliche Ausnahmebehandlung für das Exception-Objekt.

Schreibgeschützt
quelle
1
Neugierig zu wissen ... Lesen Sie dies von einem iPad, können Sie sie also nicht in Version 1.9 testen, aber einige dieser Fallstricke sind in den neuesten Ruby-Versionen nicht mehr gültig, oder?
Denis de Bernardy
12
Auch wissenswert: raiseist sehr teuer. throwist nicht. Stellen Sie sich vor throw, Sie verwenden goto, um aus einer Schleife herauszukommen.
docwhat
4
@Denis Auf welche Fallstricke beziehen Sie sich?
docwhat
1
Die Verbindung ist unterbrochen!
Morhook
Sehen Sie sich Ruby Catch-Throw und Effizienz an, um mehr über den Leistungsunterschied zu erfahren.
Franklin Yu
109
  • raise, fail, rescue, Und ensureGriff - Fehler , auch bekannt als Ausnahme
  • throwund catchsind Kontrollfluss

Im Gegensatz zu anderen Sprachen werden Rubys Wurf und Fang nicht für Ausnahmen verwendet. Stattdessen bieten sie eine Möglichkeit, die Ausführung vorzeitig zu beenden, wenn keine weiteren Arbeiten erforderlich sind. (Grimm, 2011)

Das Beenden einer einzelnen Ebene des Kontrollflusses wie eines whileRegelkreises kann mit einem einfachen Vorgang erfolgen return. Das Beenden vieler Ebenen des Kontrollflusses, wie z. B. einer verschachtelten Schleife, kann durchgeführt werden throw.

Während der Ausnahmemechanismus von Raise and Rescue großartig ist, um die Ausführung abzubrechen, wenn etwas schief geht, ist es manchmal schön, während der normalen Verarbeitung aus einem tief verschachtelten Konstrukt herausspringen zu können. Hier bieten sich Fangen und Werfen an. (Thomas und Hunt, 2001)

Verweise

  1. Grimm, Avdi. "Werfen, fangen, erheben, retten ... ich bin so verwirrt!" RubyLearning Blog. Np, 11. Juli 2011. Web. 1. Januar 2012. http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue--im-so-confused/ .
  2. Thomas, Dave und Andrew Hunt. "Ruby programmieren." : Der Pragmatische Programmierleitfaden. Np, 2001. Web. 29. September 2015. http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html .
Jared Beck
quelle
2
Avdi sieht nicht so aus, als würde er in Podcasts klingen.
hrdwdmrbl
2
Der Ruby Learning-Link scheint nicht zu funktionieren. Hier ist ein weiterer Blog-Beitrag, der die Unterschiede diskutiert: danielchangnyc.github.io/blog/2013/10/23/throw-raise
Dennis
Witzig, rubylearning.com denkt, dass Avdis Artikel immer noch da ist . Ich denke, deshalb kopieren wir Sachen nach SO, damit sie nicht verloren gehen!
Jared Beck
21

https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise bietet eine hervorragende Erklärung, die ich nicht verbessern kann. Zusammenfassend lässt sich sagen, dass ich einige Codebeispiele aus dem Blog-Beitrag geklaut habe:

  1. raise/ rescuesind die nächsten Analoga zu dem Konstrukt throw/, catchdas Sie aus anderen Sprachen kennen (oder zu Pythons raise/ except). Wenn Sie auf eine Fehlerbedingung gestoßen sind und throwdiese in einer anderen Sprache überschreiben würden , sollten Sie dies raisein Ruby tun.

  2. Rubys throw/ catchkönnen Sie die Ausführung brechen und den Stapel klettern für eine Suche catch(wie raise/ der rescueFall ist), ist aber nicht wirklich für Fehlerbedingungen gemeint. Es sollte selten verwendet werden und ist nur dann vorhanden, wenn das catchVerhalten "Gehen Sie den Stapel hinauf, bis Sie ein entsprechendes Verhalten finden " für einen Algorithmus, den Sie schreiben, Sinn macht, aber es wäre nicht sinnvoll, an das zu denkenthrow als einem Fehler entsprechend vorzustellen Bedingung.

    Wofür wird in Ruby Fangen und Werfen verwendet? bietet einige Vorschläge für nette Verwendungen des throw/ catch-Konstrukts.

Die konkreten Verhaltensunterschiede zwischen ihnen umfassen:

  • rescue Foowird Instanzen der Aufnahme von FooUnterklassen von retten Foo. catch(foo)fängt nur das gleiche Objekt ,Foo . Sie können nicht nur keinen catchKlassennamen übergeben, um Instanzen davon abzufangen, sondern auch keine Gleichheitsvergleiche durchführen. Zum Beispiel

    catch("foo") do
      throw "foo"
    end

    gibt Ihnen eine UncaughtThrowError: uncaught throw "foo"(oder eine ArgumentErrorin Versionen von Ruby vor 2.2)

  • Es können mehrere Rettungsklauseln aufgeführt werden ...

    begin
      do_something_error_prone
    rescue AParticularKindOfError
      # Insert heroism here.
    rescue
      write_to_error_log
      raise
    end

    während mehrere catches verschachtelt werden müssen ...

    catch :foo do
      catch :bar do
        do_something_that_can_throw_foo_or_bar
      end
    end
  • Ein nacktes rescueist gleichbedeutend mit rescue StandardErrorund ist ein idiomatisches Konstrukt. Ein "nackter catch" catch() {throw :foo}wird niemals etwas fangen und sollte nicht verwendet werden.

Mark Amery
quelle
Gute Erklärung, wirft aber die Frage auf, warum um alles in der Welt sie in Rubin erheben = in einer anderen Sprache werfen würden. und dann auch werfen werfen, aber es! = werfen in anderen Sprachen. Ich kann ihre ursprüngliche Logik dort nicht sehen
wired00
@ wired00 (Achselzucken) Ich stimme zu, dass es im Vergleich zu anderen populären Sprachen heute ziemlich exzentrisch erscheint.
Mark Amery
2
@ wired00: Es wurde seit den ersten Experimenten mit strukturierter Fehlerbehandlung in den 1960er Jahren als "Auslösen" einer Ausnahme bezeichnet. In den wegweisenden Artikeln, die die moderne Form der Ausnahmebehandlung erfanden, wird es als "Auslösen" einer Ausnahme bezeichnet "Auslösen" einer Ausnahme in Lisps und Smalltalks, die einige der Hauptinspirationen für Ruby waren, und es wird "Auslösen" einer Ausnahme oder "Auslösen" eines Interrupts in der Hardware genannt, bei dem das Konzept bereits vor dem Konzept einer "Programmierung" existierte Sprache "existierte. Die Frage sollte eher lauten: Warum haben diese anderen Sprachen das geändert?
Jörg W Mittag
@ MarkAmery: Denken Sie daran, dass viele dieser "anderen populären Sprachen" jünger als Ruby oder zumindest zeitgemäß sind. Die Frage sollte also eher lauten: Warum folgten diese anderen Sprachen nicht Ruby (und Smalltalk und Lisp sowie Hardware und Literatur)?
Jörg W Mittag
@ JörgWMittag Interessant - Sie haben mich zu ein wenig historischer Forschung inspiriert. C ++ hatte den Gedanken, Jahre vor Ruby eine Ausnahme zu "werfen", und laut english.stackexchange.com/a/449209/73974 geht der Begriff tatsächlich auf die 70er Jahre zurück ... also denke ich, dass wir Ruby immer noch kritisieren können etablierte Terminologie nehmen und damit etwas ganz anderes bedeuten.
Mark Amery