it-swarm.com.de

Ruby Exceptions - Warum "sonst"?

Ich versuche, Ausnahmen in Ruby zu verstehen, aber ich bin ein wenig verwirrt. Das von mir verwendete Tutorial besagt, dass Sie, wenn eine Ausnahme auftritt, die mit keiner der in den Rettungsanweisungen angegebenen Ausnahmen übereinstimmt, ein "else" verwenden können, um sie abzufangen:

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
else  
# Other exceptions
ensure
# Always will be executed
end

Allerdings habe ich auch später im Tutorial gesehen, dass "rescue" ausnahmslos verwendet wird:

begin
    file = open("/unexistant_file")
    if file
         puts "File opened successfully"
    end
rescue
    file = STDIN
end
print file, "==", STDIN, "\n"

Wenn Sie dies tun können, muss ich dann jemals müssen verwenden? Oder kann ich am Ende einfach so eine generische Rettung verwenden?

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
rescue
# Other exceptions
ensure
# Always will be executed
end
47
Kvass

Die Variable else gilt für den Fall, dass der Block abgeschlossen wird, ohne dass eine Ausnahme ausgelöst wird. Die Variable ensure wird ausgeführt, unabhängig davon, ob der Block erfolgreich abgeschlossen wurde oder nicht. Beispiel:

begin
  puts "Hello, world!"
rescue
  puts "rescue"
else
  puts "else"
ensure
  puts "ensure"
end

Dies gibt Hello, world!, dann else und dann ensure aus.

88

Hier ist ein konkreter Anwendungsfall für else in einem begin-Ausdruck. Angenommen, Sie schreiben automatisierte Tests und möchten eine Methode schreiben, die den durch einen Block ausgelösten Fehler zurückgibt. Sie möchten aber auch, dass der Test fehlschlägt, wenn der Block keinen Fehler auslöst. Du kannst das:

def get_error_from(&block)
  begin
    block.call
  rescue => err
    err  # we want to return this
  else
    raise "No error was raised"
  end
end

Beachten Sie, dass Sie den raise nicht innerhalb des begin-Blocks verschieben können, da er rescued erhält. Natürlich gibt es auch andere Möglichkeiten, else nicht zu verwenden, z. B. zu prüfen, ob errnil nach dem end ist, aber das ist nicht so knapp.

Persönlich verwende ich else selten auf diese Weise, weil ich denke, dass es selten benötigt wird, aber es ist in den seltenen Fällen nützlich.

EDIT

Ein anderer Anwendungsfall fiel mir ein. Hier ist eine typische begin/rescue:

begin
  do_something_that_may_raise_argument_error
  do_something_else_when_the_previous_line_doesnt_raise
rescue ArgumentError => e
  handle_the_error
end

Warum ist das nicht ideal? Denn die Absicht ist rescue, wenn do_something_that_may_raise_argument_errorArgumentError erhöht, nicht , wenn do_something_else_when_the_previous_line_doesnt_raise auftritt.

Normalerweise ist es besser, begin/rescue zu verwenden, um den Mindestcode, den Sie vor einem raise schützen möchten, umzuwandeln. Andernfalls gilt Folgendes:

  • sie können Fehler in dem Code maskieren, der nicht raise sein sollte.
  • die Absicht von rescue ist schwerer zu entschlüsseln. Vielleicht liest jemand (einschließlich Ihres zukünftigen Ichs) den Code und fragt sich: "Welchen Ausdruck wollte ich schützen? Es sieht wie Ausdruck ABC aus ... aber vielleicht auch Ausdruck DEF? Was wollte der Autor? ?! Refactoring wird viel schwieriger.

Sie vermeiden diese Probleme mit dieser einfachen Änderung:

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

Der else-Block in einem Beginn-Rettungs-Endblock wird verwendet, wenn Sie möglicherweise eine Ausnahme von einer Art erwarten. Wenn Sie alle erwarteten Ausnahmen durchlaufen und trotzdem nichts erhöht haben, können Sie in Ihrem other-Block alles tun, was jetzt erforderlich ist, da Sie wissen, dass Ihr ursprünglicher Code fehlerfrei lief.

1
cmwright

Der einzige Grund, den ich für den else-Block sehen kann, ist, wenn Sie etwas vor dem ensure-Block ausführen möchten, wenn der Code im begin-Block keine Fehler auslöst.

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

Dank else können Sie manchmal zwei verschachtelte begin end-Blöcke zusammenführen.
Also (vereinfachtes Beispiel aus meinem aktuellen Code) statt:

  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

du schreibst:

  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