Buzz Lightyear

Watch Out For nil in Ranges

Ruby 2.6 enhanced the syntax for Range to allow for infinite values at either end, therefore allowing them to be “endless”. The new syntax is described in the Ruby documentation.

This is great for representing concepts such as date ranges between some date and one anytime the future, but this new “sugared” syntax changes existing behaviour.

Ruby used to…

…raise an ArgumentError when a nil was passed into a range…

end_of_range = nil
(1..end_of_range).map { |i| do_something(i) }
#=> ArgumentError (bad value for range)

But now…

…beware that a Range ending in nil no longer throws an error, but represents an endless range.

end_of_range = nil
(1..end_of_range).map { |i| do_something(i) }
#=> infinite loop!

Why be careful?

Ruby 2.5, and earlier, already had the ability to create endless ranges, but only by using special explicit constants—such as Float::INFINITY.

Allowing nil to be considered a valid input to a range, in a language where the result of methods can often unexpectedly be nil, definitely gives us another place to make obscure mistakes.

Why not?

You don’t really have a choice here—it’s a part of the language.

But, you can protect yourself by ensuring that you check any value you pass into a Range. It isn’t seen as “good practice” to scatter nil checks in your code, but Ruby has never been about purity in its approach to object-oriented programming.

Last updated on May 19th, 2019 by @andycroll

An email newsletter, with one Ruby/Rails technique delivered with a ‘why?’ and a ‘how?’ every two weeks. It’s deliberately brief, focussed & opinionated.