LaunchKit
All quizzes
Ruby 0 views

Float(nil) refuses what nil.to_f forgives

Question

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

Explanation

TL;DR

Float(nil) raises TypeError: can't convert nil into Float. The Kernel conversion functions split into two families: the collection and string converters (Array(), Hash(), String()) map nil to an empty value, while the numeric ones (Float(), Integer()) are strict validators that refuse nil instead of inventing a 0.

Step by step

The forgiving converters delegate to conversion methods and treat nil as "absence of value":

Array(nil)  # => []
Hash(nil)   # => {}
Hash([])    # => {}
String(nil) # => ""
  1. Array(arg) tries to_ary, then to_a. nil.to_a is []. An object with neither method gets wrapped: Array(1) # => [1].
  2. Hash(arg) tries to_hash, special-casing nil and [] to {}. Anything else without to_hash raises TypeError.
  3. String(arg) tries to_str, then to_s. nil.to_s is "".

The numeric constructors do not fall back to to_f/to_i semantics for nil:

nil.to_f # => 0.0

begin
  Float(nil)
rescue TypeError => e
  e.message # => "can't convert nil into Float"
end

begin
  Integer(nil)
rescue TypeError => e
  e.message # => "can't convert nil into Integer"
end

The rationale: nil.to_f answers "give me your best float", and 0.0 is a guess. Float() answers "this must represent a float", and nil does not represent any number, so guessing 0.0 would silently corrupt data (think of a missing price becoming free).

Edge cases

The strictness extends to strings. Float() parses the full string or raises, where to_f happily returns garbage-tolerant defaults:

"123.45".to_f # => 123.45
"abc".to_f    # => 0.0

Float("123.45") # => 123.45

begin
  Float("abc")
rescue ArgumentError => e
  e.message # => "invalid value for Float(): \"abc\""
end

Note the two error classes: a nil argument raises TypeError (wrong type), an unparseable string raises ArgumentError (right type, wrong content).

Since Ruby 2.6, exception: false turns both failures into nil, which makes Float() a clean validation tool:

Float(nil, exception: false)   # => nil
Float("abc", exception: false) # => nil
Float("1.5", exception: false) # => 1.5

Use to_f/to_i when a default of zero is genuinely fine, and Float()/Integer() when the input is untrusted and a bad value should fail loudly (or return nil with exception: false).

Share this quiz

Comments

No comments yet. Be the first.

Only used to confirm and publish your comment. Never shown publicly, never shared.

Markdown: **bold**, `code`, ```fenced blocks```, > quotes, [links](url). HTML and images are not rendered.