279 views
What's the return value of the following Ruby 3 code?
nil.to_a # => [] Array(nil) # => [] nil.to_h # => {} Hash(nil) # => {} nil.to_s # => "" String(nil) # => "" nil.to_f # => 0.0 Float(nil) # => ???
The correct answer is
0.0
nil
It raises an error
0
Subscribe to RubyCademy and get free access to all our courses, plus hundreds of fun Ruby cards, quizzes, guides, and tutorials!
Ruby, as a dynamic and expressive language, often surprises us with the way certain elements behave.
One such case is how nil
is handled when converted to different types using Kernel
methods and the associated conversions.
If you've encountered nil
conversions in Ruby, you may have stumbled upon some unexpected behavior, especially involving methods like Float()
and Integer()
Let's explore more deeply to understand why nil conversions can be strange, useful, or even risky in different situations.
Here's the example we begin with:
nil.to_a # => [] Array(nil) # => [] nil.to_h # => {} Hash(nil) # => {} nil.to_s # => "" String(nil) # => "" nil.to_f # => 0.0 Float(nil) # => ???
The correct answer for Float(nil)
is TypeError
, which may be surprising if you were expecting a conversion similar to nil.to_f
, which returns 0.0
.
Let’s explore why this difference exists and what other conversion methods behave similarly.
nil
GracefullyRuby provides several methods to convert nil
into other types, and many of these conversions return some kind of "empty" value rather than raising an error.
Array(nil)
nil.to_a
and Array(nil)
both convert nil
into an empty array ([]
).
The method Array(arg)
first tries to call to_ary
on arg
, and if arg
does not respond to to_ary
, it then calls to_a
.
If neither method is available, it returns an array containing arg
itself. In the case of nil
, to_a
is called, resulting in []
.
This means that the absence of a value (nil
) is translated into an empty collection, which can be useful in contexts where you want to avoid handling nil
explicitly.
class MyClass; end arg = nil Array(arg) # => [] arg = [1, 2, 3] Array(arg) # => [1, 2, 3] arg = MyClass.new Array(arg) # => [#<MyClass:0x04243>]
Hash(nil)
nil.to_h
and Hash(nil)
similarly convert nil
into an empty hash ({}
).
The method Hash(arg)
tries to call to_hash
on arg
, and if arg
is nil
or an empty array, it returns an empty hash ({}
).
If arg
cannot be converted to a hash, a TypeError
is raised.
class MyClass; end arg = nil Hash(arg) # => {} arg = [[:key, :value]] Hash(arg) # => {:key=>:value} arg = MyClass.new Hash(arg) # Raises TypeError: can't convert MyClass into Hash
String(nil)
nil.to_s
and String(nil)
both convert nil
to an empty string (""
).
The method String(arg)
first tries to call to_str
on arg
, and if that fails, it calls to_s
.
For nil
, to_s
returns an empty string (""
).
class MyClass def to_s "I am an instance of MyClass" end end arg = nil String(arg) # => "" arg = MyClass.new String(arg) # => "I am an instance of MyClass"
All of these conversions share a common trait: they treat nil
as an "absence of value" that is substituted with a reasonable empty or neutral default.
nil
However, not all conversion methods are as forgiving when dealing with nil
. Consider the behavior of methods like Float()
and Integer()
.
Float(nil)
raises a TypeError
with the message "can't convert nil into Float". The method Float(arg)
attempts to convert arg
to a float by calling to_f
.
If arg
is nil
, a TypeError
is raised because nil
cannot be directly converted into a float in this context. Unlike nil.to_f
, which defaults to 0.0
, the Float()
method refuses to guess how to convert nil
and instead raises an error.
This behavior aligns with the idea of avoiding implicit assumptions about how nil
should be treated when it comes to creating a number.
arg = nil arg.to_f # => 0.0 arg = "123.45" Float(arg) # => 123.45 arg = nil Float(arg) # => TypeError: can't convert nil into Float
Integer(nil)
similarly raises a TypeError
because there is no sensible default integer representation for nil
.
arg = "123" Integer(arg) # => 123 arg = nil Integer(arg) # => TypeError
Both Float()
and Integer()
require more explicit input and prefer to raise an error if nil
is provided, as they refuse to make implicit guesses about how nil
should be treated as a number.
This aligns with programming principles like "explicit is better than implicit" and "errors should never pass silently."
Ruby's treatment of nil
during conversions can sometimes feel inconsistent, but it follows a logic that balances flexibility with safety.
Subscribe to RubyCademy and get free access to all our courses, plus hundreds of fun Ruby cards, quizzes, guides, and tutorials!
RubyCademy ©