Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Ruby, why does a equality with nil ("Date.new == nil") return nil?

When writing some rspec today, I came across some unexpected behavior with comparing Date (and Time) instances to nil. Here's a sample using raw ruby (no Rails or other libraries):

user@MacBook-Work ~ $ ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [universal-darwin10.0]
user@MacBook-Work ~ $ irb
>> 1 == nil
=> false
>> "string" == nil
=> false
>> :sym == nil
=> false
>> false == nil
=> false
>> [] == nil
=> false
>> {} == nil
=> false
>> Proc.new {} == nil
=> false

So far, so good, right?

>> Date.new == nil
=> nil
>> Time.new == nil
=> nil

Date does implement its own ===, which works fine:

>> Date.new === nil
=> false

Is there any explanation as to why this happens or why this is desired behavior? == seems to be implemented from Comparable.==, however documentation on that doesn't given any indication that it would ever return nil. What's the design decision to this?

Update! This is not the case in 1.9.2:

$ irb
ruby-1.9.2-p136 :001 > require 'date'
 => true 
ruby-1.9.2-p136 :002 > Date.new == nil
 => false 
ruby-1.9.2-p136 :003 > Time.new == nil
 => false 
like image 599
Gabe Martin-Dempesy Avatar asked Feb 08 '10 23:02

Gabe Martin-Dempesy


Video Answer


3 Answers

I checked the source and here's what I found out:

The comparison operators defined by Comparable all use the function rb_cmpint together with <=>. rb_cmpint raises an exception when one of the operands is nil.

So the operators of Comparable raise an exception if the rhs is not comparable to the lhs. I.e. 5 < 2 is false, but 5 < "la" raises an exception. They do this to differentiate between cases where < is not true because the rhs is smaller and cases where it's not true because the rhs is not comparable. Or in other words: When x < y is false that implies that x >= y is true. So in cases where that would not be the case, it throws an exception.

== raising an exception would be bad, because == usually does not (and should not) require its operands to be comparable. However == uses the same method as the other operands, which does raise an exception. So the whole function is simply wrapped in an rb_rescue. And that returns nil if an exception is thrown.

Note that this only applies to ruby 1.8. This has been fixed in 1.9 and now == never returns nil (except of course if you define your own == that does).

like image 55
sepp2k Avatar answered Nov 19 '22 08:11

sepp2k


If you're depending on this for code, you can always use the .nil? method which any Ruby Object responds to.

>> Date.new.nil?
=> false
like image 32
Peter Brown Avatar answered Nov 19 '22 08:11

Peter Brown


The Date class includes the Comparable#== method, but that method invokes the <=> method of the receiver. In this case that's Date#<=>, which expects another Date object. When it receives nil it returns nil. This behavior certainly seems inconsistent, and I don't know the reasons behind it.

like image 32
Alex Reisner Avatar answered Nov 19 '22 08:11

Alex Reisner