Quelle est la différence entre augmenter les exceptions et lancer des exceptions dans Ruby?

168

Ruby a deux mécanismes d'exceptions différents: Throw / Catch et Raise / Rescue.

Pourquoi en avons-nous deux?

Quand devriez-vous utiliser l'un et pas l'autre?

Nick Retallack
la source
«Sortir des boucles imbriquées» est un besoin courant dans de nombreux langages de programmation. Outre le gotoen C / C ++ comme @docwhat a mentionné, Java a marqué break et continue . (Python a également une proposition rejetée pour cela.)
Franklin Yu

Réponses:

104

Je pense que http://hasno.info/ruby-gotchas-and-caveats a une explication décente de la différence:

catch / throw n'est pas la même chose que relancer / sauver. catch / throw vous permet de quitter rapidement les blocs pour revenir à un point où un catch est défini pour un symbole spécifique, le sauvetage de la levée est le vrai traitement des exceptions impliquant l'objet Exception.

Lecture seulement
la source
1
Curieux de savoir ... En lisant ceci à partir d'un iPad, vous ne pouvez donc pas les tester dans la version 1.9, mais certains de ces pièges ne sont plus valables dans les versions récentes de ruby, non?
Denis de Bernardy
12
A savoir également: raisec'est très cher. thrown'est pas. Pensez throwà utiliser gotopour sortir d'une boucle.
docwhat
4
@Denis De quels pièges parlez-vous?
docwhat
1
Le lien est rompu!
morhook
Voir le lancer de rubis et l'efficacité pour en savoir plus sur la différence de performance.
Franklin Yu
110
  • raise, fail, rescueEt ensurepoignée erreurs , également connu sous le nom des exceptions
  • throwet catchsont flux de contrôle

Contrairement à d'autres langages, le throw et le catch de Ruby ne sont pas utilisés pour les exceptions. Au lieu de cela, ils fournissent un moyen de terminer l'exécution plus tôt lorsqu'aucun travail supplémentaire n'est nécessaire. (Grimm, 2011)

Terminer un seul niveau de flux de contrôle, comme une whileboucle, peut être fait avec un simple return. La terminaison de nombreux niveaux de flux de contrôle, comme une boucle imbriquée, peut être effectuée avec throw.

Bien que le mécanisme d'exception de relance et de sauvetage soit idéal pour abandonner l'exécution lorsque les choses tournent mal, il est parfois agréable de pouvoir sortir d'une construction profondément imbriquée pendant le traitement normal. C'est là que la capture et le lancer sont utiles. (Thomas et Hunt, 2001)

Références

  1. Grimm, Avdi. "Lance, attrape, soulève, sauve… Je suis tellement confus!" Blog RubyLearning. Np, 11 juillet 2011. Web. 1er janvier 2012. http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue--im-so-confused/ .
  2. Thomas, Dave et Andrew Hunt. «Programmation Ruby». : Le guide du programmeur pragmatique. Np, 2001. Web. 29 septembre 2015. http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html .
Jared Beck
la source
2
Avdi n'a pas l' air de résonner dans les podcasts.
hrdwdmrbl
2
Le lien Ruby Learning ne semble pas fonctionner. Voici un autre article de blog qui traite des différences: danielchangnyc.github.io/blog/2013/10/23/throw-raise
Dennis
Drôle, rubylearning.com pense que l'article d'Avdi est toujours là . Je suppose que c'est pourquoi nous copions des trucs vers SO, pour qu'ils ne soient pas perdus!
Jared Beck
21

https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise offre une excellente explication que je doute que je puisse améliorer. Pour résumer, en récupérant quelques exemples de code du billet de blog au fur et à mesure:

  1. raise/ rescuesont les analogues les plus proches de la construction throw/ catchque vous connaissez d'autres langages (ou de raise/ de Python except). Si vous avez rencontré une condition d'erreur et que vous la surmonteriez throwdans une autre langue, vous devriez raiseen Ruby.

  2. Ruby throw/ catchvous permet d'interrompre l'exécution et de monter dans la pile à la recherche d'un catch(comme raise/ rescuefait), mais n'est pas vraiment destiné aux conditions d'erreur. Il devrait être utilisé rarement, et est là uniquement lorsque le catchcomportement «remonter la pile jusqu'à ce que vous trouviez un correspondant » a du sens pour un algorithme que vous écrivez, mais cela n'aurait pas de sens de penser que le throwcomme correspondant à une erreur état.

    À quoi sert le catch and throw dans Ruby? propose quelques suggestions sur les bonnes utilisations de throw/ catchconstruct.

Les différences de comportement concrètes entre eux comprennent:

  • rescue Foosauvera des instances d' Fooinclusion de sous-classes de Foo. catch(foo)ne fonctionnera que pour le même objet,Foo . Non seulement vous ne pouvez pas passer catchun nom de classe pour en attraper des instances, mais cela ne fera même pas de comparaisons d'égalité. Par exemple

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

    vous donnera une UncaughtThrowError: uncaught throw "foo"(ou une ArgumentErrorversion in de Ruby antérieure à 2.2)

  • Plusieurs clauses de sauvetage peuvent être répertoriées ...

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

    alors que plusieurs catches doivent être imbriquées ...

    catch :foo do
      catch :bar do
        do_something_that_can_throw_foo_or_bar
      end
    end
  • Un nu rescueéquivaut à rescue StandardErroret est une construction idiomatique. Un "nu catch", comme catch() {throw :foo}, n'attrapera jamais rien et ne devrait pas être utilisé.

Mark Amery
la source
Bonne explication mais soulève la question, pourquoi diable voudraient-ils concevoir augmenter en rubis = jeter dans une autre langue. puis incluez également throw mais it! = throw dans d'autres langues. Je ne peux pas voir leur logique d'origine là
wired00
@ wired00 ( Shrug .) Je suis d'accord que cela semble assez excentrique par rapport aux autres langues populaires aujourd'hui.
Mark Amery
2
@ wired00: On l'appelle "lever" une exception depuis les toutes premières expériences de gestion structurée des erreurs dans les années 1960, on l'appelle "lever" une exception dans les articles fondateurs qui ont inventé la forme moderne de gestion des exceptions, on l'appelle "lever" une exception dans Lisps et Smalltalks, qui étaient quelques-unes des principales inspirations de Ruby, et cela s'appelle "lever" une exception ou "lever" une interruption dans le matériel, où le concept existait avant même le concept d'une "programmation la langue "existait. La question devrait plutôt être: pourquoi ces autres langues ont-elles changé cela?
Jörg W Mittag
@MarkAmery: Souvenez-vous que beaucoup de ces "autres langues populaires" sont plus jeunes que Ruby ou du moins contemporaines. Donc, la question devrait plutôt être: pourquoi ces autres langages n'ont-ils pas suivi Ruby (et Smalltalk et Lisp et le matériel et la littérature).
Jörg W Mittag
@ JörgWMittag Intéressant - vous m'avez inspiré pour faire un peu de recherche historique. C ++ avait la notion de "lancer" une exception des années avant l'arrivée de Ruby, et selon english.stackexchange.com/a/449209/73974, le terme remonte en fait aux années 70 ... donc je pense que nous pouvons encore critiquer Ruby pour prendre une terminologie établie et l'utiliser pour signifier quelque chose de complètement différent.
Mark Amery