Ryan Davis Ruby QuickRef dit (sans explication) :
Ne pas sauver l'exception. JAMAIS. ou je te poignarde.
Pourquoi pas ? Quelle est la bonne chose à faire ?
Ryan Davis Ruby QuickRef dit (sans explication) :
Ne pas sauver l'exception. JAMAIS. ou je te poignarde.
Pourquoi pas ? Quelle est la bonne chose à faire ?
TL;DR : Utiliser StandardError
à la place pour la capture des exceptions générales. Lorsque l'exception originale est soulevée à nouveau (par exemple, lors d'un sauvetage pour enregistrer l'exception seulement), le sauvetage de Exception
est probablement correct.
Exception
est la racine de La hiérarchie des exceptions de Ruby donc quand vous rescue Exception
que vous sauvez de tout y compris les sous-classes telles que SyntaxError
, LoadError
y Interrupt
.
Sauvetage Interrupt
empêche l'utilisateur d'utiliser CTRLC pour quitter le programme.
Sauvetage SignalException
empêche le programme de répondre correctement aux signaux. Il sera impossible de le tuer, sauf par kill -9
.
Sauvetage SyntaxError
signifie que eval
qui échouent le feront en silence.
Tout cela peut être démontré en exécutant ce programme, et en essayant de CTRLC o kill
il :
loop do
begin
sleep 1
eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
rescue Exception
puts "I refuse to fail or be stopped!"
end
end
Sauvetage de Exception
n'est même pas le défaut. Faire
begin
# iceberg!
rescue
# lifeboats
end
ne sauve pas de Exception
il sauve de StandardError
. Vous devez généralement spécifier quelque chose de plus précis que la valeur par défaut de l'option StandardError
mais en sauvant de Exception
élargit le site le champ d'application plutôt que de le réduire, et peut avoir des résultats catastrophiques et rendre la recherche de bogues extrêmement difficile.
Si vous avez une situation où vous voulez sauver de la StandardError
et que vous avez besoin d'une variable avec l'exception, vous pouvez utiliser ce formulaire :
begin
# iceberg!
rescue => e
# lifeboats
end
ce qui est équivalent à :
begin
# iceberg!
rescue StandardError => e
# lifeboats
end
L'un des rares cas communs où il est sain de sauver des Exception
c'est à des fins de journalisation/rapport, auquel cas vous devez immédiatement relancer l'exception :
begin
# iceberg?
rescue Exception => e
# do some logging
raise # not enough lifeboats ;)
end
Si vous relancez l'exception, alors c'est bien puisque vous ne l'avalez pas, mais que vous essayez juste de savoir que cela s'est produit et de le laisser remonter. C'est généralement le cas pour la journalisation.
Ce conseil est bon pour un environnement Ruby propre. Mais malheureusement, un certain nombre de gemmes ont créé des exceptions qui descendent directement de Exception. Notre environnement en compte 30 : par exemple OpenID::Server::EncodingError, OAuth::InvalidRequest, HTMLTokenizerSample. Il s'agit d'exceptions que l'on voudrait vraiment attraper dans des blocs de sauvetage standard. Malheureusement, rien dans Ruby n'empêche ou même ne décourage les gems d'hériter directement d'Exception -- même le nommage est peu intuitif.
El réel La règle est : Ne pas jeter les exceptions. L'objectivité de l'auteur de votre citation est douteuse, comme en témoigne le fait qu'elle se termine par
ou je te poignarde
Bien sûr, il faut savoir que les signaux (par défaut) lèvent des exceptions, et que les processus qui s'exécutent depuis longtemps sont normalement interrompus par le biais d'un signal. Ne faites donc pas cela :
#! /usr/bin/ruby
while true do
begin
line = STDIN.gets
# heavy processing
rescue Exception => e
puts "caught exception #{e}! ohnoes!"
end
end
Non, vraiment, ne le fais pas. Ne le fais même pas pour voir si ça marche.
Cependant, disons que vous avez un serveur threadé et que vous voulez que toutes les exceptions ne soient pas :
thread.abort_on_exception = true
).Dans ce cas, cela est parfaitement acceptable dans votre fil de gestion des connexions :
begin
# do stuff
rescue Exception => e
myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
myLogger.error("Stack trace: #{backtrace.map {|l| " #{l}\n"}.join}")
end
Ce qui précède est une variante du gestionnaire d'exceptions par défaut de Ruby, avec l'avantage qu'il ne tue pas votre programme. Rails fait cela dans son gestionnaire de requête.
Les exceptions de signal sont soulevées dans le thread principal. Les threads d'arrière-plan ne les reçoivent pas, il est donc inutile d'essayer de les attraper à cet endroit.
C'est particulièrement utile dans un environnement de production, où l'on fait pas vous voulez que votre programme s'arrête simplement dès que quelque chose ne va pas. Vous pouvez alors prendre les vidages de pile dans vos journaux et ajouter à votre code pour traiter les exceptions spécifiques plus bas dans la chaîne d'appel 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 :
a = do_something rescue "something else"
Dans cette ligne, si do_something
lève une exception, elle est attrapée par Ruby, jetée, et a
est attribué "something else"
.
En général, ne faites pas cela, sauf dans des cas particuliers où vous connaître vous n'avez pas besoin de vous inquiéter. Un exemple :
debugger rescue nil
El debugger
est un moyen plutôt agréable de définir un point d'arrêt dans votre code, mais si elle est exécutée en dehors d'un débogueur, et de Rails, elle lève une exception. En théorie, vous ne devriez pas laisser traîner du code de débogage dans votre programme (pff ! personne ne fait ça !) mais vous pourriez vouloir le garder là pendant un certain temps pour une raison ou une autre, sans pour autant lancer continuellement votre débogueur.
Note :
Si vous avez exécuté le programme de quelqu'un d'autre qui attrape les exceptions de signal et les ignore (disons le code ci-dessus), alors.. :
pgrep ruby
o ps | grep ruby
recherchez le PID de votre programme incriminé, puis exécutez kill -9 <PID>
.Si vous travaillez avec le programme de quelqu'un d'autre qui, pour une raison ou une autre, est truffé de ces blocs d'exceptions ignorées, le fait de placer ce bloc en haut de la ligne principale est une échappatoire possible :
%W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
Cela amène le programme à répondre aux signaux de fin normaux en se terminant immédiatement, en contournant les gestionnaires d'exceptions, sans nettoyage . Cela peut donc entraîner une perte de données ou autre. Faites attention !
Si vous devez le faire :
begin
do_something
rescue Exception => e
critical_cleanup
raise
end
vous pouvez vraiment le faire :
begin
do_something
ensure
critical_cleanup
end
Dans le second cas, critical cleanup
sera appelé à chaque fois, qu'une exception soit levée ou non.
Désolé, c'est faux. Un serveur doit nunca sauver l'exception et ne rien faire d'autre que de l'enregistrer. Cela le rendra inefficace, sauf par les moyens suivants kill -9
.
Vos exemples dans la note 3 ne sont pas équivoques. ensure
s'exécutera, qu'une exception soit levée ou non, tandis que l'option rescue
ne s'exécutera que si une exception a été levée.
Ils ne sont pas /exactement/ équivalents mais je n'arrive pas à trouver comment exprimer succinctement l'équivalence d'une manière qui ne soit pas laide.
Parce que cela capture toutes les exceptions. Il est peu probable que votre programme puisse se remettre de tout d'entre eux.
Vous ne devez gérer que les exceptions dont vous savez comment vous remettre. Si vous n'anticipez pas un certain type d'exception, ne la gérez pas, plantez bruyamment (écrivez les détails dans le journal), puis diagnostiquez les journaux et corrigez le code.
Avaler des exceptions est mauvais, ne faites pas ça.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
35 votes
Alors vous pourriez probablement écrire votre propre :)
74 votes
Je suis très mal à l'aise avec l'appel à la violence ici. C'est juste un programme.
1 votes
Jetez un coup d'œil à cet article en Ruby Exception avec une belle Hiérarchie des exceptions en Ruby .
2 votes
Parce que Ryan Davis va te poignarder. Alors les enfants. Ne sauvez jamais les exceptions.
7 votes
@DarthEgregious Je ne peux pas vraiment dire si vous plaisantez ou pas. Mais je pense que c'est hilarant. (Et ce n'est évidemment pas une menace sérieuse). Maintenant, chaque fois que je pense à attraper l'Exception, je me demande si cela vaut la peine d'être poignardé par un type aléatoire sur Internet.
0 votes
Qu'est ce que tu veux dire par "je demande juste dans l'espoir que quelqu'un écrive une bonne réponse" ? tu ne comprends pas à quoi sert stack overflow ! c'est pour les questions dont tu veux connaître la réponse, pas juste pour demander ce qui obtient le plus de votes !
0 votes
@RaphaelSpoerri Ce n'est pas vrai -- voir meta.stackoverflow.com/q/300121/52207 et les autres méta questions qui y sont liées.