Ruby: lazy evaluation

Ruby ist ein faules Miststück. Das folgende Verhalten, genannt “lazy evaluation” war mir neu und hat mich einige Nerven gekostet:

1
2
3
if (@account.save && @user.save)
  ...
end

Erwarten würde man, das beide Methoden aufgerufen werden, dessen Rückgabe Werte logisch-und verknüpft werden und das Ergebnis ausgewertet wird. Stimmt aber nicht. Ruby tut nämlich das hier:

The and and && operators evaluate their first operand. If false, the expression returns false; otherwise, the expression returns the value of the second operand.

Ruby führt also den zweiten Operand nur aus, wenn der erste Operand true zurück gibt. Und es gibt hierbei auch keine Interpretation, sondern es wird einfach der zweite Operand ausgegeben. Das führt in obigem Beispiel dazu, dass @user.save erst ausgeführt wird, wenn @account.save true zurück gab. Aber es wird noch verrückter:

1
true && "Test"

Was kommt raus? Natürlich “Test”. Den wenn der erste Operand true ergibt, gibt Ruby den zweiten zurück. Und Test wird auf True evaluiert, was folgendes Beispiel verdeutlicht:

1
"test" && false

Gibt uns false zurück. Den “test” => true, also wieder der zweite Operand zurück gegeben.

Zum Abschluss noch das Gegenbeispiel:

1
false && "Egal"

Gibt uns false.

Tja, und was tun wir nun wegen dem Ursprungsproblem? Folgende Zeile schafft Abhilfe:

1
if (@account.save && @user.save) || @user.save

Somit werden auf jedenfall beide Methoden aufgerufen, was für die Datenvalidierung in Rails wichtig ist. Aber das ist ein anderes Thema.

Nicht schlimm also, aber gut zu wissen was Ruby da tut.

Update
Die beste Lösung ist in der Tat folgende:

1
if (@account.save & @user.save)

In diesem Fall werden beide Operanden auf jedenfall ausgewertet. Danke Mario für den Hinweis.