При записи некоторого rspec сегодня, я столкнулся с некоторым неожиданным поведением со сравнением Даты (и Время) экземпляры к нолю. Вот демонстрационный рубин сырых данных использования (никакие направляющие или другие библиотеки):
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
Пока неплохо, правильно?
>> Date.new == nil
=> nil
>> Time.new == nil
=> nil
Дата действительно реализует свой собственный ===, который хорошо работает:
>> Date.new === nil
=> false
Там какое-либо объяснение относительно того, почему это происходит или почему это - желаемое поведение? ==, кажется, реализован от Сопоставимого. ==, однако документация относительно этого не делает, учитывая никакой признак, что она когда-либо возвращала бы ноль. Что проектное решение к этому?
Обновление! Дело обстоит не так в 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
Я проверил источник и вот что выяснил:
Все операторы сравнения, определенные Comparable, используют функцию rb_cmpint
вместе с <=>
. rb_cmpint
вызывает исключение, когда один из операндов равен нулю.
Таким образом, операторы Comparable вызывают исключение, если rhs не сопоставимо с lhs. Т.е. 5 <2
ложно, но 5 <"la"
вызывает исключение. Они делают это, чтобы различать случаи, когда <
неверно, потому что правая сторона меньше, и случаи, когда она неверна, потому что правая сторона несопоставима. Или другими словами: когда x
x> = y
истинно. Поэтому в случаях, когда это не так, он генерирует исключение.
==
создание исключения было бы плохим, потому что ==
обычно не требует (и не должен) требовать, чтобы его операнды были сопоставимы. Однако ==
использует тот же метод, что и другие операнды, что вызывает исключение. Таким образом, вся функция просто заключена в rb_rescue
. И это возвращает nil
, если возникает исключение.
Обратите внимание, что это относится только к рубину 1.8.Это было исправлено в версии 1.9, и теперь ==
никогда не возвращает nil
(кроме, конечно, если вы определяете свой собственный ==
, который это делает).
Класс Date включает метод Comparable # ==
, но этот метод вызывает метод <=>
приемника. В данном случае это Date # <=>
, который ожидает другой объект Date. Когда он получает nil
, он возвращает nil
. Такое поведение определенно кажется непоследовательным, и я не знаю его причин.
Если вы зависите от этого кода, вы всегда можете использовать .nil? метод, на который реагирует любой объект Ruby.
>> Date.new.nil?
=> false
Это происходит потому, что вы не можете сравнивать вещи, которые не определены. Это желательно, потому что, если хотя бы один из ваших операндов не определен, вы не сможете сделать никаких выводов о результате, которые отличаются от утверждения истины.
Многие языки трактуют nil и false одинаково, что вызывает подозрение исключительно из соображений удобства. Это определенно неверно с математической точки зрения.