When it comes to check if an item is included in a Range, most Ruby programmers tend to use the include? method only. Probably this is because the include? method comes with the Enumerable module and therefore we are used to have it available in several classes. But the Range class comes with a less known friend that is very useful to have in our programming Swiss army knife. This method is called Range#cover?. Lets see what the Ruby documentation says:

Range#include?(obj)

 

From the documentation:

Returns true if obj is an element of the range, false otherwise. If begin and end are numeric, comparison is done according to the magnitude of the values.

 

Range#cover?(obj)

 

From the documentation:

Returns true if the object is between the begin and the end of the range. This tests begin <= obj <= end when exclude_end? is false and begin <= obj < end when exclude_end? is true.

 

As you can see, both methods check if an item is included in the range, but in some cases they do it in a very different way. Range#cover? is related to the Comparable module. It checks if the item is within the range using comparison methods, that is, if the item is greater or equal to the beginning of the range and lower (or equal) to the end of the range. In the other hand, Range#include?, for some types like numerics, relies on the same private method used by Range#cover?(at the moment of this writing r_cover_p) and for others like String, it performs the check by iterating across the range’s items and checking if the item is one of them.

For Strings, in general, Range#cover? will be much faster than Range#include?, but in some cases the results aren’t the ones you would expect. Lets see it with some examples:

As you can see the difference is notorious. You could say, lets use always cover?, it is much faster! But sadly we can’t always rely on cover?

I don’t know you, but I wouldn’t expect 'dog' to be within 'a'..'z'. The reason is, as we mentioned above, that cover? uses comparison, so it is true that 'a' >= 'dog' and 'dog' <= 'z'. If we check the items, we can verify that 'dog' isn’t included:

I agree that the String examples above may be a little bit contrived. I did them in that way in order to show you how long can it take to iterate across the range and some other unexpected cases, but most times this isn’t a problem. I personally like to use Range#cover? to check within date ranges, but for other cases I don’t have a clear favourite, instead I would think about that particular case and decide which one to use with the information I have at that moment.