Ruby QuickRef de Ryan Davis dit (sans explication):
Ne sauvez pas l'exception. DÉJÀ. ou je te poignarderai.
Pourquoi pas? Quelle est la bonne chose à faire?
Ruby QuickRef de Ryan Davis dit (sans explication):
Ne sauvez pas l'exception. DÉJÀ. ou je te poignarderai.
Pourquoi pas? Quelle est la bonne chose à faire?
Réponses:
TL; DR : utilisez à la
StandardError
place pour la capture d'exceptions générales. Lorsque l'exception d'origine est re-levée (par exemple lors d'un sauvetage pour enregistrer l'exception uniquement), le sauvetageException
est probablement correct.Exception
est la racine de la hiérarchie des exceptions de Ruby , donc quand vousrescue Exception
vous sauver de tout , y compris les sous - classes telles queSyntaxError
,LoadError
etInterrupt
.Le sauvetage
Interrupt
empêche l'utilisateur d'utiliser CTRLCpour quitter le programme.Le sauvetage
SignalException
empêche le programme de répondre correctement aux signaux. Ce sera impossible à tuer sauf parkill -9
.Le sauvetage
SyntaxError
signifie queeval
les échecs se feront en silence.Tous ces éléments peuvent être affichés en exécutant ce programme, et en essayant de CTRLCou
kill
il:Le sauvetage
Exception
n'est même pas la valeur par défaut. Fairene sauve pas
Exception
, il sauveStandardError
. Vous devez généralement spécifier quelque chose de plus spécifique que la valeur par défautStandardError
, mais le sauvetageException
élargit la portée plutôt que de la réduire, et peut avoir des résultats catastrophiques et rendre la recherche de bogues extrêmement difficile.Si vous avez une situation dans laquelle vous souhaitez effectuer un sauvetage
StandardError
et que vous avez besoin d'une variable à l'exception, vous pouvez utiliser ce formulaire:ce qui équivaut à:
L'un des rares cas courants où il est raisonnable de sauver
Exception
est à des fins de journalisation / génération de rapports, auquel cas vous devez immédiatement lever à nouveau l'exception:la source
Throwable
en javaADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
et puisrescue *ADAPTER_ERRORS => e
La vraie règle est: ne jetez pas les exceptions. L'objectivité de l'auteur de votre citation est discutable, comme en témoigne le fait qu'elle se termine par
Bien sûr, sachez que les signaux (par défaut) lèvent des exceptions, et que les processus de longue durée se terminent par un signal, donc intercepter une exception et ne pas terminer sur des exceptions de signal rendra votre programme très difficile à arrêter. Alors ne fais pas ça:
Non, vraiment, ne le fais pas. Ne lancez même pas cela pour voir si cela fonctionne.
Cependant, supposons que vous ayez un serveur threadé et que vous ne souhaitiez pas que toutes les exceptions:
thread.abort_on_exception = true
).Ensuite, cela est parfaitement acceptable dans votre thread de gestion de connexion:
Ce qui précède correspond à une variante du gestionnaire d'exceptions par défaut de Ruby, avec l'avantage qu'il ne tue pas également votre programme. Rails le fait dans son gestionnaire de requêtes.
Les exceptions de signal sont levées dans le thread principal. Les fils d'arrière-plan ne les obtiendront pas, il est donc inutile d'essayer de les attraper là-bas.
Ceci est particulièrement utile dans un environnement de production, où vous ne voulez pas que votre programme s'arrête simplement en cas de problème. Ensuite, vous pouvez prendre les vidages de pile dans vos journaux et ajouter à votre code pour traiter des exceptions spécifiques plus bas dans la chaîne d'appels et d'une manière plus gracieuse.
Notez également qu'il existe un autre idiome Ruby qui a à peu près le même effet:
Dans cette ligne, si
do_something
lève une exception, il est attrapé par Ruby, jeté eta
attribué"something else"
.Généralement, ne faites pas cela, sauf dans des cas particuliers où vous savez que vous n'avez pas à vous inquiéter. Un exemple:
le
debugger
fonction est un moyen plutôt agréable de définir un point d'arrêt dans votre code, mais si elle s'exécute en dehors d'un débogueur et de Rails, elle déclenche une exception. Maintenant, théoriquement, vous ne devriez pas laisser traîner du code de débogage dans votre programme (pff!Remarque:
Si vous avez exécuté le programme de quelqu'un d'autre qui intercepte les exceptions de signal et les ignore (dites le code ci-dessus), alors:
pgrep ruby
oups | grep ruby
recherchez le PID de votre programme incriminé, puis exécutezkill -9 <PID>
.Si vous travaillez avec le programme de quelqu'un d'autre qui, pour quelque raison que ce soit, est parsemé de ces blocs ignorer les exceptions, le mettre en haut de la ligne principale est une sortie possible:
Cela oblige le programme à répondre aux signaux de terminaison normaux en se terminant immédiatement, en contournant les gestionnaires d'exceptions, sans nettoyage . Cela pourrait donc entraîner une perte de données ou similaire. Faites attention!
Si vous devez le faire:
vous pouvez réellement faire ceci:
Dans le second cas,
critical cleanup
sera appelé à chaque fois, qu'une exception soit levée ou non.la source
kill -9
.ensure
qu'il y ait une exception levée ou non, tandis que le testrescue
ne fonctionnera que si une exception a été déclenchée.TL; DR
Ne le faites pas
rescue Exception => e
(et ne relancez pas l'exception) - ou vous pourriez quitter un pont.Disons que vous êtes dans une voiture (avec Ruby). Vous avez récemment installé un nouveau volant avec le système de mise à niveau over-the-air (qui utilise
eval
), mais vous ne saviez pas que l'un des programmeurs s'était trompé sur la syntaxe.Vous êtes sur un pont et vous vous rendez compte que vous vous dirigez un peu vers la balustrade, alors tournez à gauche.
Oops! Ce n'est probablement pas bon ™, heureusement, Ruby soulève un
SyntaxError
.La voiture devrait s'arrêter immédiatement - non?
Nan.
Vous remarquez quelque chose ne va pas, et vous slam sur les pauses d'urgence (
^C
:Interrupt
)Ouais - ça n'a pas beaucoup aidé. Vous êtes assez près du rail, alors vous mettez la voiture en stationnement (
kill
ing:)SignalException
.À la dernière seconde, vous retirez les clés (
kill -9
), et la voiture s'arrête, vous claquez vers l'avant dans le volant (l'airbag ne peut pas se gonfler parce que vous n'avez pas gracieusement arrêté le programme - vous l'avez terminé), et l'ordinateur à l'arrière de votre voiture claque dans le siège en face d'elle. Une canette à moitié pleine de Coke déborde sur les papiers. L'épicerie à l'arrière est écrasée et la plupart sont recouvertes de jaune d'oeuf et de lait. La voiture doit être sérieusement réparée et nettoyée. (Perte de données)J'espère que vous avez une assurance (sauvegardes). Oh ouais - parce que l'airbag ne s'est pas gonflé, vous êtes probablement blessé (être renvoyé, etc.).
Mais attendez! Il y a
plusraisons pour lesquelles vous voudrez peut-être utiliserrescue Exception => e
!Disons que vous êtes cette voiture et que vous voulez vous assurer que l'airbag se gonfle si la voiture dépasse sa vitesse d'arrêt en toute sécurité.
Voici l'exception à la règle: vous ne pouvez intercepter
Exception
que si vous relancez l'exception . Donc, une meilleure règle est de ne jamais avalerException
et de toujours relancer l'erreur.Mais l'ajout de sauvetage est à la fois facile à oublier dans un langage comme Ruby, et mettre une déclaration de sauvetage juste avant de soulever un problème semble un peu sec. Et vous ne voulez pas oublier la
raise
déclaration. Et si vous le faites, bonne chance pour essayer de trouver cette erreur.Heureusement, Ruby est génial, vous pouvez simplement utiliser le
ensure
mot - clé, qui s'assure que le code s'exécute. Leensure
mot-clé exécutera le code quoi qu'il arrive - si une exception est levée, si ce n'est pas le cas, la seule exception étant la fin du monde (ou d'autres événements improbables).Boom! Et ce code devrait fonctionner de toute façon. La seule raison que vous devez utiliser
rescue Exception => e
est si vous avez besoin d'accéder à l'exception ou si vous souhaitez uniquement que le code s'exécute sur une exception. Et n'oubliez pas de relancer l'erreur. À chaque fois.Remarque: Comme l'a souligné @Niall, assurez-vous qu'il fonctionne toujours . C'est bien parce que parfois votre programme peut vous mentir et ne pas lever d'exceptions, même lorsque des problèmes surviennent. Avec les tâches critiques, comme le gonflage des airbags, vous devez vous assurer que cela se produit quoi qu'il arrive. Pour cette raison, vérifier chaque fois que la voiture s'arrête, si une exception est levée ou non, est une bonne idée. Même si le gonflage des airbags est une tâche peu courante dans la plupart des contextes de programmation, cela est en fait assez courant avec la plupart des tâches de nettoyage.
la source
ensure
comme alternative àrescue Exception
est trompeuse - l'exemple implique qu'ils sont équivalents, mais comme indiquéensure
se produira qu'il y ait une exception ou non, alors maintenant vos airbags se déploieront parce que vous avez dépassé 5 mph, même si rien ne s'est mal passé.Parce que cela capture toutes les exceptions. Il est peu probable que votre programme puisse récupérer de l' un d'eux.
Vous ne devez gérer que les exceptions dont vous savez comment récupérer. Si vous ne prévoyez pas un certain type d'exception, ne le gérez pas, plantez fort (écrivez les détails dans le journal), puis diagnostiquez les journaux et corrigez le code.
Avaler des exceptions est mauvais, ne faites pas ça.
la source
C'est un cas spécifique de la règle que vous ne devez pas détecter d' exception que vous ne savez pas comment gérer. Si vous ne savez pas comment le gérer, il est toujours préférable de laisser une autre partie du système l'attraper et le gérer.
la source
Je viens de lire un excellent article de blog à ce sujet sur honeybadger.io :
Ruby Exception vs StandardError: Quelle est la différence?
la source