ruby-mine

exploring the mine

rescue - Rubys Rettungsring für kleine Unfälle

von murphy am 12.03.2006 (16 Uhr)

Heute ist Syntax-Sonntag! Ab jetzt soll es jede Woche eine neue Kurzgeschichte aus der Syntaxwelt von Ruby geben. Heute:

Der rescue-Modifikator.

Man hat einen einfachen begin-rescue-Block, der etwa folgende Form besitzt:

begin
  clas = Object.const_get 'Foo'
rescue
  clas = nil
end

Das kann man vereinfachen, indem man die linke Seite ausklammert:

clas = begin
  Object.const_get 'Foo'
rescue
  nil
end

Oder man benutzt rescue in der Modifikator-Variante, wie if oder unless:

clas = Object.const_get 'Foo' rescue nil

Die Anweisung probiert also zuerst, die Konstante Object::Foo zu finden. Wird sie nicht gefunden, wird ein NameError (uninitialized constant Foo) erzeugt; diesen schluckt der Modifikator und liefert stattdessen nil.

Ein typisches Einsatzgebiet ist die Division mit Werten, die 0 sein könnten:

a, b = 3, 0
q = a/b  #-> ZeroDivisionError
q = a/b rescue 0  #-> 0

Im Code von ActionMailer habe ich auch folgendes gefunden:

rline = line.dup rescue line

Hier wird versucht, ein Objekt zu duplizieren, bevor es verwendet wird; das ist natürlich sicherer, falls das Objekt zum Beispiel von außen verändert werden könnte (wie bei einer Iteration per each) oder wenn man Bang-methoden (gsub! etc.) aufrufen will. Wenn das Dublizieren fehlschlägt, wird stillscheigend das Original benutzt. Bin mir nicht sicher, ob das so gut ist.

Vorsicht

Die Präzedenzregeln sind hier vielleicht etwas überraschend. Bei einer Zuweisung bindet rescue stärker als das Gleichheitszeichen (geändert seit Ruby 1.8); will man jedoch eine Methode mit einem solchen Argument aufrufen, gibt es Probleme:

p a / b rescue 0  #-> nichts wird ausgegeben...
(p a / b) rescue 0  #-> ...weil Ruby den Ausdruck so auswertet
p((a / b rescue 0))  #-> 0

Geschwindigkeit

Gute Nachricht für rastlose Performance-Fanatiker: Ein Modifikator hinter einer Anweisung scheint die Geschwindigkeit nicht zu beeinflussen:

require 'benchmark'

N = 100_000
Benchmark.bm 6 do |bm|
  bm.report 'normal' do N.times do
    Array(1..5).reverse.sort
  end end
  bm.report 'rescue' do N.times do
    Array(1..5).reverse.sort rescue false
  end end
end
__END__
            user     system      total        real
normal  1.422000   0.000000   1.422000 (  2.033000)
rescue  1.522000   0.000000   1.522000 (  1.983000)


Kommentar schreiben

Name (notwendig)

Mail (wird nicht veröffentlicht)

Webseite


Kommentare

  1. Murphy schrieb am 12.03.2006 (16 Uhr)

    Das verbesserte Script zum Highlighten sorgt dafür, dass die WordPress die Anführungszeichen in Ruhe lässt.

  2. Olli schrieb am 13.03.2006 (11 Uhr)

    ich muss sagen die variante clas = begin Object.const_get 'Foo' rescue nil end wirkt unuebersichtlich. dann ist der normale geschriebene block schon besser find ich persoenlich. ausser man ist schreibfaul - warum sollte man diese unuebersichtliche variante nutzen?

  3. bovi schrieb am 13.03.2006 (11 Uhr)

    Ich würde sagen es spart die doppelte Verwendung des Bezeichners :class. Syntax sugar halt. Btw. interessanter Artikel, besonders der Hinweis auf die ActionMailer Bibliothek. Allgemein scheint es zum guten Ton zu gehören Fehlermeldungen einfach zu unterdrücken. Ich kann nicht so ganz beurteilen wie problematisch das werden kann, im Moment scheint es ja noch gut zu funktionieren. Mein Tipp ist jedoch, für erwartete Zustände auch eine Annahme im Code aufzuführen. Wenn ich z.B. über eine Datei iterieren dann ist ganz klar, dass ich irgendwann EOF bekomme. Da muss man das Programm doch nicht unbedingt auf eine Exception laufen lassen.

  4. Ruby-Mine » Blog Archive » [2050°C] Der Anfang schrieb am 20.04.2006 (02 Uhr)

    [...] Murphy hat Euch in den letzten Wochen, einen tiefen Einblick in die syntaktischen Finessen von Ruby geliefert. Ihr habt normale Strings und Fancy Strings kennengelernt. Es stellte sich heraus, dass Here Docs ebenfalls nur Strings, mit einer zusätzlichen Dimension, sind. Die funktionale Programmierung mit proc Objekten macht Euch das Leben um einiges leichter. Und ein exception handling mit rescue ist nun zu einem selbstverständlichen Codefragment geworden, welches jeden unsicheren Code schmückt. Parallel hierzu möchten wir euch nun auf einer neuen Ebene einen Einblick in unseren Rubin verschaffen. Hierzu fahren wir die Schmelzanlage auf knapp 2050°C hoch und schauen uns an, was sich im inneren des Edelsteins befindet. [...]

  5. Andreas schrieb am 15.03.2008 (22 Uhr)

    Schön. Sowas wie: rescue Mist, Dreck also Mist oder Dreck, geht auch. Hatte ich in keiner Doku gefunden. Falls jemand hier danach sucht.