3

Given that Time objects cannot be compared with Fixnum without explicit casting:

0 <= Time.now # => ArgumentError: comparison of Fixnum with Time failed
Time.now <= 10000000000 # => ArgumentError: comparison of Time with 10000000000 failed

and what the documentation for Range#cover? says,

cover?(obj)true or false

Returns true if obj is between the begin and end of the range.

This tests begin <= obj <= end when exclude_end? is false and begin <= obj < end when exclude_end? is true.

I expect:

(0...10000000000).cover?(Time.now) # => false

to raise an exception rather than silently return false. Why doesn't it raise an exception?

It is understandable that, with explicit casting, the comparison works:

(0...10000000000).cover?(Time.now.to_i) # => true
sawa
  • 165,429
  • 45
  • 277
  • 381
  • 1
    `range_cover` uses `r_less` and r_less returns a positive value for non-comparable things: https://github.com/stulentsev/ruby/blob/62b750bb55916e526fb53cb4c19da777872c27f6/range.c#L174-L187 – Sergio Tulentsev Feb 19 '16 at 15:04
  • @SergioTulentsev I am not that good in reading Ruby C-code. Does it mean that the doc is wrong? Can you elaborate that as an answer? – sawa Feb 19 '16 at 15:10

2 Answers2

2

The doc doesn't mention an implementation detail. range_cover is implemented in terms of r_less (via r_cover_p). And r_less comment says:

/* compares _a_ and _b_ and returns:
 * < 0: a < b
 * = 0: a = b
 * > 0: a > b or non-comparable
 */

Here is the source of r_cover_p:

static VALUE
r_cover_p(VALUE range, VALUE beg, VALUE end, VALUE val)
{
  if (r_less(beg, val) <= 0) {
    int excl = EXCL(range);
    if (r_less(val, end) <= -excl)
      return Qtrue;
  }
  return Qfalse;
}

As we can see, a positive number returned from either of r_less invocations will result in a Qfalse.

Now, the reason why the doc doesn't mention it, I think, is to keep it light. Normally (99.9999% of cases), you're supposed to compare comparable things, right? And in the odd case you don't, you still get a correct answer ("this Time does not belong to this range of integers").

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
0

I am pretty sure both .include? and .cover? uses the case quality operator (===) so the value you get is the same as:

p Time.now === 1000 #=> false
hirolau
  • 13,451
  • 8
  • 35
  • 47
  • I see. Then it looks like a documentation bug, at least. But does that mean `Range#include?` and `Range#cover?` are the same? I suppose not. – sawa Feb 19 '16 at 14:35
  • According to "The Ruby Programming language book", `Range#cover?` in v1.9 same as `Range#include?` in 1.8. In 1.9 they are different. – Ilya Feb 19 '16 at 14:44
  • `cover?`, unlike `include?` doesn't use any equality operators. This is its strength: O(1) runtime. – Sergio Tulentsev Feb 19 '16 at 15:08