2012-12-04 13 views
5
x = StandardError.new(:hello) 
y = StandardError.new(:hello) 
x == y # => true 
x === y # => true 

begin 
    raise x 
rescue x 
    puts "ok" # gets printed 
end 

begin 
    raise x 
rescue y 
    puts "ok" # doesn't get printed 
end 

Warum wird das zweite "ok" nicht gedruckt? Ich kann es nicht herausfinden. Ich habe here gelesen, dass Ruby den === Operator benutzt, um Ausnahmen zu den Rettungsklauseln zusammenzubringen, aber das ist angeblich nicht der Fall.Was ist falsch an diesem Rettungsbeispiel?

Ich bin mit Ruby 1.9.3

EDIT: Also es so scheint nach raise x, x == y und x === y nicht mehr tun halten. Es scheint, weil x und yno longer have the same backtrace.

+0

, das heißt, "fängt jede Standarderror und Subklassen, und legt die Instanz in die Variable y". 'y' wird nicht als Wert interpretiert (wie ich es möchte), sondern als Variablenname. – Norswap

+0

Ich denke, wenn Sie versuchen, Muster Übereinstimmungen auf RFID-Tags zu tun, ist die "Raise/Catch" Idiom möglicherweise nicht der beste Weg zu gehen. Was ist mit einer 'Fall'-Anweisung oder einem dynamischen Versand mit Mustern? –

+0

Die Anwendung verfügt über eine "Low-Level" -Logik, die mit der Karte kommuniziert. Hier werden die Ausnahmen ausgelöst. Darüber hinaus gibt es eine höhere Anwendungslogik. Einige der vom Tag zurückgegebenen Fehler sind tatsächlich ziemlich hoch (z. B. "die Datei ist bereits auf dem Tag vorhanden") und muss dem Benutzer wieder zur Verfügung gestellt werden. Ausnahmen scheinen die beste Option dafür zu sein. – Norswap

Antwort

1

Ich möchte nur etwas zu der Tabelle hinzuzufügen: OP-Code legt nahe, dass die beiden Ausnahmen sind die gleichen, aber sie sind nicht - außerdem will ich zeigen, welche OP gemeint mit:

So es so scheint, Nach dem Erhöhen von x halten x == y und x === y nicht länger. Es scheint, weil x und y nicht mehr das gleiche Backtrace haben. No

x = StandardError.new(:hello) 
y = StandardError.new(:hello) 
class Object 
    def all_equals(o) 
    ops = [:==, :===, :eql?, :equal?] 
    Hash[ops.map(&:to_s).zip(ops.map {|s| send(s, o) })] 
    end 
end 

puts x.all_equals y # => {"=="=>true, "==="=>true, "eql?"=>false, "equal?"=>false} 

begin 
    raise x 
rescue 
    puts "ok" # gets printed 
end 

puts x.all_equals y # => {"=="=>false, "==="=>false, "eql?"=>false, "equal?"=>false} 
0

EDIT: das Problem für zukünftige Antworten Um zu klären, weil ich meine Antwort ist falsch zu denken, hier die Subtilität ist, dass x und yInstanzen sind statt Klassen, und normalerweise würden Sie Klassen in einem verwenden raise Aussage.


Wenn das gewünschte Verhalten in dem zweiten Rettungs drucken ist, die y den Trick nicht. Sie verursachen eine Ausnahme der Klasse x, und Sie haben keine Rescue-Klausel, die x behandelt. Sie würden „ok“ gedruckt für den zweiten Block sehen, wenn Sie StandardError gefangen, die gemeinsame Basisklasse, aber:

begin 
    raise x 
rescue StandardError 
    puts "ok" 
end 

In Bezug auf # ===, ich denke, das Problem ist, dass, wenn Sie erhöhen, Sie zu tun mit einer Instanz von x anstelle von x als eine Klasse.

+0

Ich weiß das, aber es ist nicht meine Frage. Meine Frage ist: "Warum' 'rette x' fang 'x', während' retty y' nicht? ". Du hast nicht erklärt, warum 'rettung x'' x' fängt. Außerdem haben Sie einen Tippfehler in Ihrer Antwort, da ich "keine Ausnahme der Klasse' x' "erstelle (' x' ist einfach die Variable, die die Ausnahme enthält, die der Klasse 'StandardError' entspricht). Ich bin mir nicht sicher, was du meintest. – Norswap

+0

@Norswap: 'x' ist eine Variable, die einen Verweis auf eine Klasse enthält. Die Sache, die gefangen wird, ist eine Instanz dieser Klasse. –

+0

Nein, es enthält keinen Verweis auf eine Klasse. 'x.class => StandardError'. Es ist eine Instanz der Klasse 'StandardError'. – Norswap

0

Es scheint, dass die Definition für die Rettung ist:

[rescue [error_type [=> var],..] 

Weder x noch y ist stricly ein error_type. Sie sind Instanzen eines Fehlertyps. Ich glaube nicht, dass Sie wirklich gültigen Code ausführen, so wie Sie es dort tun.

Wenn Sie laufen:

begin 
    raise x 
rescue y.class 
    puts "ok" 
end 

Dann wird es wie erwartet.

Beachten Sie auch, dass auf Ruby 1.8 weder x == y noch x === ytrue zurückgibt.

+0

Ich werde die Frage bearbeiten, ich verwende Ruby 1.9.3 – Norswap

+0

@Norswap Aber immer noch; was genau versuchst du zu tun?Ich gehe davon aus, dass du nur herumspielst, denn was du im Grunde machst, ist wie "Rettung 1" zu sagen. Es macht keinen Sinn. Du solltest Klassen retten, keine Instanzen. – Casper

+0

@Casper: Ich denke, der Code ist gültig (d. H., Wenn es kompiliert, was es tut). Ob es klar ist, ist eine andere Frage. Aber ich denke nicht, dass das für die Frage relevant ist. –

2

Ich denke, das ist ein Bug, oder eher eine Unterspezifikation von Ruby 1.9. Beachten Sie, dass Ruby 2.0 eine

TypeError: class or module required for rescue clause 

auf den Linien 8 und 14

Hinweis wirft, dass die raise nicht unbedingt tun, was Sie denken, es tut, auch nicht.Wenn Sie raise ein Objekt, erhöhst du nicht wirklich dass Objekt Sie ein neues Objekt erhöhen, die von dem Objekt aufgebaut ist, Sie an diesen einfachen Regeln, nach geben:

  • , wenn das Objekt auf exception antwortet rufen, exception auf das Objekt und erhöhen den Rückgabewert
  • , wenn das Objekt eine Unterklasse von Exception ist, rufen new und erhöhen den Rückgabewert
  • sonst fehlschlagen
  • versagen auch, wenn der Rückgabewert von einer der oben genannten Methoden ist keine Instanz von Exception

Also, Sie sind nichtx tatsächlich erhöhen, Sie heben x.exception. Gemäß der Dokumentation von Exception#exception ist x.exception jedoch x.