(nil..nil).cover?(x) is true for all x since beginless ranges were introduced
The introduction of beginless ranges in #14799 changed the behaviour of
Range#cover? when both the beginning and the end of a
nil. Previously such a range would cover no values; now it covers all values:
irb-2.6.6> (nil..nil).cover?("23 dogs") => false irb-2.7.0> (nil..nil).cover?("23 dogs") => true
This change was noted at the time, but it was believed that the call would have previously raised an error instead of returning
false. I've not exhaustively checked, but the old behaviour goes back to at least ruby 2.2.
I'm not sure that either of these results are "correct", since I'm not sure that the concept of a range with no beginning or end is meaningful. Without either parameter, it seems impossible to infer the values it ranges over, since we have no type information available.
However, I did find both the current result and the change in behaviour quite surprising, so I've opened this issue.
Some options I considered:
- Do nothing: a beginless-and-endless range is unbounded in both value and type, so covers all values of all types
- Reinstate the old behaviour: a beginless-and-endless range has no type, so covers no values
- Raise an error: a beginless-and-endless range is meaningless, so constructing one should raise an error
My personal preference is option 3, but I can clearly see that it represents the most effort. I'd welcome people's thoughts; if option 1 is preferred, I'd be happy to contribute a documentation patch to make the new behaviour clear.
Updated by jeremyevans0 (Jeremy Evans) over 1 year ago
I'm definitely against option 3. It would break backwards compatibility for code that expects Ruby 2.7 behavior:
start = params['start'] finish = params['finish'] (start..finish).cover? obj.modified_date
There are libraries that at least have tests for ranges unbounded in both directions (Sequel does, I would guess Rails does as well), and those would break if option 3 was taken.
I'm fine with the current behavior, and don't consider it a bug. I'm not strongly against option 2, though.
Updated by eightbitraptor (Matthew Valentine-House) over 1 year ago
For interests sake, I've put together a short patch that implements option 2: Reinstating the old behaviour of returning false only when the range has
nil at the beginning and the end. All other behaviour should remain the same.
Updated by sawa (Tsuyoshi Sawada) 10 months ago
I do not understand the logic behind option 2. I would rather expect this logic:
a beginless-and-endless range has no type, so it imposes no restriction on the type
which would lead us to option 1. Its coverage should return
true for any object.
To my understanding, the begin value restricts the range by imposing a minimum (type and value), and the end value restricts the range by imposing a maximum (type and value). Lacking one of them means it is restricted only in one end, lacking both of them should mean it is restricted in neither end.