Ruby Quiz

377 views

What's the return value of the following Ruby 3 code?

r = (1.0..3.0)

r === 3 # ???

The correct answer is

true

false

nil

It raises ArgumentError

Unlock Your Ruby Potential

Subscribe to RubyCademy and get free access to all our courses, plus hundreds of fun Ruby cards, quizzes, guides, and tutorials!

Explanation

r = (1.0..3.0)
r === 3

At first sight you might think that the comparison will fail because the range was built from floats and you are testing it with an integer. Ruby, however, defines === on ranges in a way that uses comparison rather than strict type equality. The goal of === for ranges is to check if a value falls within the start and end boundaries of the range.

Let's walk through it carefully.

r = (1.0..3.0)

This creates a range that starts at 1.0 and ends at 3.0. It includes both ends since we used .. and not .... The object stored in r is an instance of Range.

r === 3

Here Ruby calls the === method on the range. For ranges, === checks if the argument is between the beginning and the end. The integer 3 is compared with 1.0 and 3.0. Ruby knows how to compare integers and floats, so it promotes the integer to a float for the comparison. 3 is greater than or equal to 1.0 and less than or equal to 3.0, so the result is true.

By the way, if you enjoy these kinds of deep Ruby explanations, RubyCademy offers many more resources to help you master subtle behaviors like this one.

You can try similar experiments:

(1.0..3.0) === 2     # true, 2 is between 1.0 and 3.0
(1.0..3.0) === 3.0   # true, 3.0 is the upper bound and is included
(1.0..3.0) === 4     # false, 4 is outside the range

Ruby makes no strict type distinction here because both integers and floats are numeric and comparable. This can be surprising if you expect === to behave like ==. For ranges, === is designed for case equality checks such as inside a case expression.

score = 85

case score
when 0..59
  "Fail"
when 60..100
  "Pass"
end
# => "Pass"

Here the integer 85 matches the range 60..100 even though the range is defined with integers. If the range had been floats, it would still match because Ruby compares the values numerically.

To confirm the behavior with types, consider:

(1.0..3.0).include?(3)  # true
(1.0..3.0).include?(3.5) # false
(1..3).include?(2.5)     # true, because 2.5 lies between 1 and 3

Both === and include? operate on the principle of order comparison, not strict class checking. The important detail is that Ruby treats integers and floats as comparable, so they interact smoothly in ranges.

The return value for the original code is true.

Unlock Your Ruby Potential

Subscribe to RubyCademy and get free access to all our courses, plus hundreds of fun Ruby cards, quizzes, guides, and tutorials!

RubyCademy ©