web-dev-qa-db-fra.com

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
47
Kvass

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.

88

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 rescued. 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
5
Kelvin

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.

1
cmwright

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
0
Magne

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
0
Nakilon