Ruby Language
Ausnahmen mit Begin / Rescue abfangen
Suche…
Ein grundlegender Fehlerbehandlungsblock
Lassen Sie uns eine Funktion erstellen, um zwei Zahlen zu teilen. Das ist sehr vertrauensvoll hinsichtlich der Eingabe:
def divide(x, y)
return x/y
end
Dies wird bei vielen Eingaben gut funktionieren:
> puts divide(10, 2)
5
Aber nicht alles
> puts divide(10, 0)
ZeroDivisionError: divided by 0
> puts divide(10, 'a')
TypeError: String can't be coerced into Fixnum
Wir können die Funktion umschreiben, indem wir den riskanten Teilungsvorgang in einen begin... end
Block einschließen, um nach Fehlern zu suchen, und eine rescue
, um eine Nachricht auszugeben und bei Problemen nil
.
def divide(x, y)
begin
return x/y
rescue
puts "There was an error"
return nil
end
end
> puts divide(10, 0)
There was an error
> puts divide(10, 'a')
There was an error
Fehler speichern
Sie können den Fehler speichern, wenn Sie ihn in der rescue
möchten
def divide(x, y)
begin
x/y
rescue => e
puts "There was a %s (%s)" % [e.class, e.message]
puts e.backtrace
end
end
> divide(10, 0)
There was a ZeroDivisionError (divided by 0)
from (irb):10:in `/'
from (irb):10
from /Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'
> divide(10, 'a')
There was a TypeError (String can't be coerced into Fixnum)
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `eval'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/context.rb:380:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:489:in `block (2 levels) in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:623:in `signal_status'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:486:in `block in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:246:in `block (2 levels) in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `loop'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `block in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:485:in `eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:395:in `block in start'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `start'
/Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'
Auf andere Fehler prüfen
Wenn Sie abhängig von der Art des Fehlers unterschiedliche Aktionen ausführen möchten, verwenden Sie mehrere rescue
mit jeweils einem anderen Fehlertyp als Argument.
def divide(x, y)
begin
return x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
return nil
rescue TypeError
puts "Division only works on numbers!"
return nil
end
end
> divide(10, 0)
Don't divide by zero!
> divide(10, 'a')
Division only works on numbers!
Wenn Sie den Fehler für die Verwendung im rescue
speichern möchten:
rescue ZeroDivisionError => e
Verwenden Sie eine rescue
ohne Argument, um Fehler eines Typs abzufangen, der nicht in einer anderen rescue
ist.
def divide(x, y)
begin
return x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
return nil
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
end
end
> divide(nil, 2)
Don't do that (NoMethodError)
In diesem Fall ist der Versuch, nil
durch 2 zu teilen, kein ZeroDivisionError
oder TypeError
wird er von der Standard- rescue
, die eine Meldung NoMethodError
, um uns NoMethodError
, dass es sich um einen NoMethodError
.
Wiederholen
In einer rescue
können Sie erneut retry
, die begin
Klausel erneut auszuführen, vermutlich nachdem Sie den Umstand geändert haben, der den Fehler verursacht hat.
def divide(x, y)
begin
puts "About to divide..."
return x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
y = 1
retry
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
end
end
Wenn wir Parameter übergeben, von denen wir wissen, dass sie einen TypeError
, wird die begin
Klausel ausgeführt (hier mit dem Ausdruck "About to divide" gekennzeichnet), und der Fehler wird wie zuvor abgefangen und nil
wird zurückgegeben:
> divide(10, 'a')
About to divide...
Division only works on numbers!
=> nil
Aber wenn wir Parameter übergeben , die eine verursachen ZeroDivisionError
, die begin
wird Klausel ausgeführt, der Fehler abgefangen wird, der Divisor von 0 auf 1 geändert, und dann retry
Sie es begin
retry
Ursachen der begin
Block wieder ausgeführt werden (von oben), jetzt mit ein verschiedene y
. Beim zweiten Mal gibt es keinen Fehler und die Funktion gibt einen Wert zurück.
> divide(10, 0)
About to divide... # First time, 10 ÷ 0
Don't divide by zero!
About to divide... # Second time 10 ÷ 1
=> 10
Überprüfen, ob kein Fehler aufgetreten ist
Sie können eine else
Klausel für Code verwenden, der ausgeführt wird, wenn kein Fehler auftritt.
def divide(x, y)
begin
z = x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
else
puts "This code will run if there is no error."
return z
end
end
Die else
Klausel wird nicht ausgeführt, wenn ein Fehler auftritt, der die Kontrolle an eine der rescue
überträgt:
> divide(10,0)
Don't divide by zero!
=> nil
Wenn jedoch kein Fehler auftritt, wird die else
Klausel ausgeführt:
> divide(10,2)
This code will run if there is no error.
=> 5
Beachten Sie, dass die else
Klausel nicht ausgeführt wird, wenn Sie von der begin
Klausel zurückkehren
def divide(x, y)
begin
z = x/y
return z # Will keep the else clause from running!
rescue ZeroDivisionError
puts "Don't divide by zero!"
else
puts "This code will run if there is no error."
return z
end
end
> divide(10,2)
=> 5
Code, der immer ausgeführt werden sollte
Verwenden Sie eine ensure
, wenn es Code gibt, den Sie immer ausführen möchten.
def divide(x, y)
begin
z = x/y
return z
rescue ZeroDivisionError
puts "Don't divide by zero!"
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
ensure
puts "This code ALWAYS runs."
end
end
Die ensure
Klausel wird ausgeführt, wenn ein Fehler auftritt:
> divide(10, 0)
Don't divide by zero! # rescue clause
This code ALWAYS runs. # ensure clause
=> nil
Und wenn kein Fehler vorliegt:
> divide(10, 2)
This code ALWAYS runs. # ensure clause
=> 5
Die Sicherheitsklausel ist nützlich, wenn Sie beispielsweise sicherstellen möchten, dass Dateien geschlossen werden.
Beachten Sie, dass im Gegensatz zu der else
Klausel, die ensure
Klausel vor der Ausführung begin
oder rescue
Klausel gibt einen Wert. Wenn die ensure
Klausel hat eine return
, die den außer Kraft gesetzt wird return
einer anderen Klausel!