Ruby Exceptions - Pourquoi "else"?
J'essaie de comprendre les exceptions dans Ruby mais je suis un peu confus. Le tutoriel que j'utilise indique que, si une exception se produit qui ne correspond à aucune des exceptions identifiées par les instructions de secours, vous pouvez utiliser un "else" pour l'intercepter:
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# Other exceptions
ensure
# Always will be executed
end
Cependant, j'ai aussi vu plus tard dans le tutoriel "rescue" être utilisé sans exception spécifiée:
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
file = STDIN
end
print file, "==", STDIN, "\n"
Si vous pouvez le faire, est-ce que j’ai besoin d’utiliser autre chose? Ou puis-je simplement utiliser un sauvetage générique à la fin, comme ceci?
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
rescue
# Other exceptions
ensure
# Always will be executed
end
La else
est pour quand le bloc se termine sans exception levée. La ensure
est exécutée, que le bloc se termine avec succès ou non. Exemple:
begin
puts "Hello, world!"
rescue
puts "rescue"
else
puts "else"
ensure
puts "ensure"
end
Ceci imprimera Hello, world!
, puis else
, puis ensure
.
Voici un cas d'utilisation concret pour else
dans une expression begin
. Supposons que vous écriviez des tests automatisés et que vous souhaitiez écrire une méthode renvoyant l'erreur générée par un bloc. Mais vous souhaitez également que le test échoue si le blocage ne génère pas d'erreur. Tu peux le faire:
def get_error_from(&block)
begin
block.call
rescue => err
err # we want to return this
else
raise "No error was raised"
end
end
Notez que vous ne pouvez pas déplacer raise
à l'intérieur du bloc begin
, car il obtiendra rescue
d. Bien sûr, il existe d'autres moyens de ne pas utiliser else
, comme vérifier si err
est nil
après le end
, mais ce n'est pas aussi succinct.
Personnellement, j’utilise rarement else
de cette manière, car j’estime que c’est rarement nécessaire, mais cela s’avère utile dans ces rares cas.
MODIFIER
Un autre cas d'utilisation m'est venu à l'esprit. Voici une begin
/rescue
typique:
begin
do_something_that_may_raise_argument_error
do_something_else_when_the_previous_line_doesnt_raise
rescue ArgumentError => e
handle_the_error
end
Pourquoi est-ce moins qu'idéal? L'intention étant de rescue
lorsque do_something_that_may_raise_argument_error
lève ArgumentError
, pas lorsque do_something_else_when_the_previous_line_doesnt_raise
est levé.
Il est généralement préférable d'utiliser begin
/rescue
pour envelopper le code minimum que vous voulez protéger d'une raise
, car sinon:
- vous pouvez masquer des bugs dans le code qui n'était pas supposé
raise
- l'intention de
rescue
est plus difficile à déchiffrer. Quelqu'un (y compris votre futur individu) peut lire le code et se demander "Quelle expression est-ce que je voulais protéger? Il ressemble à l'expression ABC ... mais peut-être à l'expression DEF aussi ???? Quel était l'intention de l'auteur?! " La refactorisation devient beaucoup plus difficile.
Vous évitez ces problèmes avec ce simple changement:
begin
do_something_that_may_raise_argument_error
rescue ArgumentError => e
handle_the_error
else
do_something_else_when_the_previous_line_doesnt_raise
end
Le bloc else
dans un bloc d'extrémité begin rescue est utilisé lorsque vous prévoyez peut-être qu'une exception quelconque se produira. Si vous passez en revue toutes vos exceptions attendues mais que rien n'a encore été soulevé, vous pouvez faire tout ce qui est nécessaire dans votre bloc else, maintenant que vous savez que votre code d'origine a fonctionné sans erreur.
La seule raison pour laquelle je peux voir le bloc else
est si vous voulez exécuter quelque chose avant le bloc ensure
lorsque le code du bloc begin
ne génère aucune erreur.
begin
puts "Hello"
rescue
puts "Error"
else
puts "Success"
ensure
puts "my old friend"
puts "I've come to talk with you again."
end
Grâce à else
, vous pouvez parfois fusionner deux blocs begin end
imbriqués.
So (exemple simplifié de mon code actuel) au lieu de:
begin
html = begin
NetHTTPUtils.request_data url
rescue NetHTTPUtils::Error => e
raise unless 503 == e.code
sleep 60
retry
end
redo unless html["market"]
end
vous écrivez:
begin
html = NetHTTPUtils.request_data url
rescue NetHTTPUtils::Error => e
raise unless 503 == e.code
sleep 60
retry
else
redo unless html["market"]
end