Quelle est la différence entre «lever« foo »» et «lever Exception.new (« foo »)»?

103

Quelle est la différence - technique, philosophique, conceptuelle ou autre - entre

raise "foo"

et

raise Exception.new("foo")

?

John Bachir
la source

Réponses:

121

Techniquement, le premier déclenche une RuntimeError avec le message défini sur "foo", et le second déclenche une exception avec le message défini sur "foo".

En pratique, il existe une différence significative entre le moment où vous voudriez utiliser le premier et le moment où vous souhaitez utiliser le second.

En termes simples, vous voulez probablement pas un RuntimeErrorfichier Exception. Un bloc de secours sans argument attrapera RuntimeErrors, mais n'attrapera PAS Exceptions. Donc, si vous augmentez un Exceptiondans votre code, ce code ne l'attrapera pas:

begin
rescue
end

Pour attraper le, Exceptionvous devrez faire ceci:

begin
rescue Exception
end

Cela signifie qu'en un sens, an Exceptionest une erreur "pire" que a RuntimeError, car vous devez faire plus de travail pour vous en remettre.

Donc, ce que vous voulez dépend de la façon dont votre projet gère ses erreurs. Par exemple, dans nos démons, la boucle principale a un sauvetage vide qui les attrapera RuntimeErrors, les rapportera, puis continuera. Mais dans une ou deux circonstances, nous voulons que le démon meure vraiment en cas d'erreur, et dans ce cas, nous lançons un Exception, qui passe directement par notre "code normal de gestion des erreurs" et sort.

Et encore une fois, si vous écrivez du code de bibliothèque, vous voulez probablement un RuntimeError, pas un Exception, car les utilisateurs de votre bibliothèque seront surpris si cela génère des erreurs qu'un rescuebloc vide ne peut pas détecter , et il leur faudra un moment pour comprendre pourquoi.

Enfin, je dois dire que le RuntimeErrorest une sous-classe de la StandardErrorclasse, et la règle actuelle est que même si vous pouvez utiliser raise n'importe quel type d'objet, le blanc rescuene capturera par défaut que tout ce qui hérite de StandardError. Tout le reste doit être spécifique.

Daniel Lucraft
la source
2
très instructif, merci. quelques petites choses: [1] Ce dernier paragraphe est le plus éclairant, et laissez - moi de découvrir quelque chose que vous ne irb pas mentionné: RuntimeError < StandardError < Exception[2] Par conséquent, ce deuxième bloc de code va attraper à la fois une exception et un RuntimeError [3] il est intéressant / étrange que la relance et le sauvetage "nus" fonctionnent avec cette exception particulière [4]. La règle de base est peut-être d'élever RuntimeError dans le code client, mais de lever et de sauver ses propres exceptions personnalisées dans son propre code?
John Bachir
1
[1, 2] Oui. [3] pas sûr ... [4] Quand je codifie à mon niveau le plus professionnel, j'ai tendance à créer des types d'erreur personnalisés qui héritent de StandardError. Cela n'a pas à être plus compliqué que quelques lignes comme class MissingArgumentsError < StandardError; end.
Daniel Lucraft
Très informatif, mais dans quel genre de situations vous voudrez lever une exception plutôt qu'une erreur d'exécution si l'erreur d'exécution est préférée pour l'écriture de libraray?
Chihung Yu
35

De la documentation officielle:

raise   
raise( string )
raise( exception [, string [, array ] ] )

Sans argument, lève l'exception $!ou déclenche un RuntimeErrorif $!est nul. Avec un seul Stringargument, il déclenche un RuntimeErroravec la chaîne comme message. Sinon, le premier paramètre doit être le nom d'une Exceptionclasse (ou d'un objet qui renvoie une Exceptionexception lors de l'envoi). Le deuxième paramètre facultatif définit le message associé à l'exception et le troisième paramètre est un tableau d'informations de rappel. Les exceptions sont capturées par la clause de sauvetage des begin...endblocs.

raise "Failed to create socket"
raise ArgumentError, "No parameters", caller
ennuikiller
la source