Nate Dalo

February 4, 2025

Conversion Functions in Ruby

In Ruby, a conversion function converts to the type indicated by the function name, if possible. Technically, it is a method on the Kernel class, but it starts with a capital letter and is used like a function.

Integer("1") # => 1
Float("3.14") # => 3.14

Why would you use this over the more common methods such as to_i and to_f?  One reason is that the conversion functions will raise an exception if the conversion cannot happen, whereas the explicit conversion methods will sometimes result in surprising behavior:

nil.to_i            # => 0
"some string".to_i  # => 0 

The conversion functions will raise an exception when the value cannot be coerced:

Integer("foo")

# ArgumentError: invalid value for Integer(): "foo"

Furthermore, the conversion functions can let you eloquently handle other cases that might require a lot more code.  For example, when dealing with an array as input, you can give some flexibility by using the Array conversion function to coerce nil and single value elements in an array easily.  Check it out:

def some_method(array)
  array.each do |element|
    puts element
  end
end

For example, this won't work if you pass in nil or a string.  You will get an error:

NoMethodError: undefined method `each' for an instance of String
NoMethodError: undefined method `each' for nil

With a minor tweak, you can convert nil to be an empty array or a single instance of a String, Float, etc. to be wrapped inside an Array using the conversion function for Array.

def some_method(array)
  Array(array).each do |element|
    puts element
  end
end

This works because the conversion function for Array does the following:

Array(nil)   # => []
Array("foo") # => ["foo"]
Array([1,2]) # => [1, 2]

You can also write your own conversion functions.  Let's say we have a Duration class to represent some unit of time that we want to standardize in seconds:

def Duration(input)
  case input
  when Integer
    input # Already in seconds
  when String
    if input =~ /^(\d+)m$/ # Matches "10m" for 10 minutes
      $1.to_i * 60
    elsif input =~ /^(\d+)h$/ # Matches "2h" for 2 hours
      $1.to_i * 3600
    elsif input =~ /^(\d+)s$/ # Matches "30s" for 30 seconds
      $1.to_i
    else
      raise ArgumentError, "Invalid duration format: #{input}"
    end
  when Duration # when already a duration, just return it
    input
  else
    raise TypeError, "Cannot convert #{input.class} to duration"
  end
end

puts Duration(120)             # => 120
puts Duration("10m")           # => 600
puts Duration("2h")            # => 7200
puts Duration("30s")           # => 30
puts Duration(Duration("10m")) # => 600
puts Duration("invalid")       # Raises ArgumentError

In summary, conversion functions can result in safer, more eloquent code!