Exceptions bieten ebenso wie throw und catch die Möglichkeit,
den Programmablauf nicht lokal zu beeinflussen. Allerdings sollten sie aus
semantischen Gründen wirklichen Ausnahmesituationen im Sinne von Fehlern
vorbehalten bleiben. Sie ermöglichen eine Trennung zwischen dem "`normalen"'
Algorithmus und der Behandlung von darin hervorgerufenen Fehlern.
In der folgenden Syntaxübersicht bezeichnen "zitierte" Begriffe
wörtlich einzugebenden Text, [geklammerte] Ausdrücke sind optional, sind
sie mit + markiert, können sie mehrfach wiederholt werden. Bei
den <so> markierten Stellen fügt man eigenen Code bzw. eigene Klassen
ein.
"begin" <heikler Code> [rescue [<AusnahmeKlasse> ["," <AusnahmeKlasse>]+] ["=>" <Variable>] <Fehlerbehandlungs-Code> ]+ ["else" <Dieser Code wird nur ausgeführt, wenn keine Ausnahme aufgetreten ist> ] ["ensure" <Dieser Code wird in jedem Fall ausgeführt> ] "end"
begin print 1 raise "Test-Exception" print 2 rescue print 3 else print 5 ensure print 4 end #-> 134
Der optionale ensure-Teil dient dazu, unbedingt
notwendige Aufräumarbeiten auszuführen und wird auf
jeden Fall ausgeführt. Der ebenfalls optionale else-Zweig
wird nur verwendet, wenn zwischen begin und rescue
keine Ausnahme ausgelöst worden ist. rescue ohne Exception-Angabe
dient als "`Default-Handler"', der alle Exceptions bearbeitet, die vorher
nicht aufgefangen wurden.
In den Behandlungsblöcken kann man mit retry
die Ausführung des heiklen Codeabschnitts wiederholen.
i = 1
begin
print "Login (#{i}): "
user = gets.chomp
raise "Einbruch!" if user != "ruby"
rescue RuntimeError
puts "#{user} kenn' ich nicht."
i += 1
retry
else
puts "Willkommen!"
end
# ergibt
Login (1): java
java kenn' ich nicht.
Login (2): ruby
Willkommen!
![]()
rescue fängt alle StandardError- und davon abgeleitete
Ausnahmen (wie zum Beispiel RuntimeError) ab, wenn kein expliziter
Ausnahmetyp angegeben wird.
Es gibt jedoch auch Ausnahme(-Klasse)n, welche nicht von RuntimeError abgeleitet
sind: ScriptError, NoMemoryError etc.
raise kann auch mit
einer bestimmten Ausnahme-Klasse aufgerufen
werden, um unterschiedliche Typen von Ausnahmen zu erzeugen,
die getrennt behandelt werden können. Alle Ausnahme-Klassen - also auch
eigens definierte - müssen vonException oder einer ihrer Unterklassen
abgeleitet sein. Ein raise ohne Zusatz erzeugt eine RuntimeError-Ausnahme.
Ruby durchsucht die rescue-Zweige, bis ein Zweig mit der Klasse der Ausnahme
oder einer ihrer Oberklassen gefunden wird.
begin print 1 raise ArgumentError, "Test-Exception" print 2 rescue ArgumentError print 5 rescue print 3 ensure print 4 end #-> 154
Mit begin/rescue können auch besondere Tastaturereignisse
abgefangen werden, zum Beispiel der
Tastendruck Strg+C, mit dem man unter Unix ein Programm abbricht:
begin loop do end rescue Interrupt puts "Abbruch" end
Im folgenden Beispiel erzielen wir den gleichen Effekt, fangen aber
mit der trap-Methode das Signal auf, bevor es Ruby in seiner
DEFAULT-Interrupt-Routine in eine
Interrupt-Ausnahme wandelt und uns diese per raise
zukommen lässt.
begin
trap("INT"){ raise }
while true
end
rescue
puts "Abbruch"
trap("INT", "DEFAULT")
end
Das trap im rescue-Zweig stellt das
Standardverhalten wieder her.