Converting and Coercing Objects to Different Types
Problem
You have an object of one type and you want to use it as though it were of another type.
Solution
You might not have to do anything at all. Ruby doesn't enforce
type safety unless the programmer has explicitly written it in. If your original class defines the same methods as the class you were thinking of converting it to, you might be able to use your object as is.
If you do have to convert from one class to another, Ruby provides conversion methods for most common paths:
"4".to_i # => 4
4.to_s # => "4"
Time.now.to_f # => 1143572140.90932
{ "key1" => "value1", "key2" => "value2" }.to_a
# => [["key1", "value1"], ["key2", "value2"]]
If all else fails, you might be able to manually create an instance of the new class, and set its instance variables using the old
data.
Discussion
Some programming languages have a "cast" operator that forces the compiler to treat an object of one type like an object of another type. A cast is usually a programmer's assertion that he knows more about the
types of
objects than the compiler. Ruby has no
cast operator. From Ruby's perspective, type checking is just an extra hoop you have to jump through. A cast operator would make it easier to jump through that hoop, but Ruby omits the hoop altogether.
Wherever you're tempted to cast an object to another type, you should be able to just do nothing. If your object can be used as the other type, there's no problem: if not, then casting it to that type wouldn't have helped anyway.
Here's a concrete example. You probably don't need to convert a hash into an array just so you can pass it into an iteration method that expects an array. If that method only calls each on its argument, it doesn't really "expect an array:" it expects a reasonable implementation of each. Ruby hashes provide that implementation just as well as arrays.
def print_each(array)
array.each { x puts x.inspect }
end
hash = { "pickled peppers" => "peck of",
"sick sheep" => "sixth" }
print_each(hash.to_a)
# ["sick sheep", "sixth"]
# ["pickled peppers", "peck of"]
print_each(hash)
# ["sick sheep", "sixth"]
# ["pickled peppers", "peck of"]
Ruby does provide methods for
converting one data type into another. These methods follow the naming convention to_[other type], and they usually create a brand new object of the new type, but containing the old data. They are generally used when you want to use some method of the new data type, or display or store the data in another format.
In the case of print_each, not converting the hash to an array gives the same results as converting, and the code is shorter and faster when it doesn't do the conversion. But converting a hash into an array of keyvalue pairs does let you call methods defined by Array but not by Hash. If what you really want is an arraysomething ordered, something you can modify with push and popthere's no reason not to convert to an array and stop using the hash.
array = hash.to_a
# => [["sick sheep", "sixth"], ["pickled peppers", "peck of"]]
# Print out a tonguetwisting invoice.
until array.empty?
item, quantity = array.pop
puts "#{quantity} #{item}"
end
# peck of pickled peppers
# sixth sick sheep
Some methods convert one data type to another as a side effect: for instance, sorting a hash implicitly converts it into an array, since hashes have no notion of ordering.
hash.sort
# => [["pickled peppers", "peck of"], ["sick sheep", "sixth"]]
Number conversion and coercion
Most of the commonly used conversion methods in stock Ruby are in the number classes. This makes sense because arithmetic operations can give different results depending on the numeric types of the inputs. This is one place where Ruby's conversion methods are used as a substitute for casting. Here, to_f is used to force Ruby to perform floatingpoint division instead of integer division:
3/4 # => 0
3/4.to_f # => 0.75
Integers and floatingpoint
numbers have to_i and to_f methods to convert back and forth between each other. BigDecimal or Rational
objects define the same methods; they also define some brand new conversion methods: to_d to convert a number to BigDecimal, and to_r to convert a number to Rational. To convert to or from Rational objects you just have to require 'rational'. To convert to or from BigDecimal objects you must require 'bigdecimal' and also require 'bigdecimal/utils'.
require 'rational'
Rational(1, 3).to_f # => 0.333333333333333
Rational(11, 5).to_i # => 2
2.to_r # => Rational(2, 1)
Here's a table that shows how to convert between Ruby's basic numeric types.
 Integer  Floatingpoint  BigDecimal  Rational 

Integer  to_i(identity)  to_f  to_r.to_d  to_r  Float  to_i(decimal discard)  to_f (new)  to_d  to_d.to_r (include bigdecimal/util)  BigDecimal  to_i  to_f  to_d (new)  to_r (include bigdecimal/util)  Rational  to_i(dec discard)  to_f (approx)  to_d (include bigdecimal/util)  to_r (identity) 
Two cases deserve special mention. You can't convert a floatingpoint number directly into rational number, but you can do it through BigDecimal. The result will be imprecise, because floatingpoint numbers are imprecise.
require 'bigdecimal'
require 'bigdecimal/util'
one_third = 1/3.0 # => 0.333333333333333
one_third.to_r
# NoMethodError: undefined method 'to_r' for 0.333333333333333:Float
one_third.to_d.to_r # => Rational(333333333333333, 1000000000000000)
Similarly, the best way to convert an Integer to a BigDecimal is to convert it to a rational number first.
20.to_d
# NoMethodError: undefined method 'to_d' for 20:Fixnum
20.to_r.to_d # => #<BigDecimal:b7bfd214,'0.2E2',4(48)>
When it needs to perform arithmetic operations on two numbers of different types, Ruby uses a method called coerce. Every numeric type implements a coerce method that takes a single number as its argument. It returns an array of two numbers: the object itself and the argument passed into coerce. Either or both numbers might undergo a conversion, but whatever happens, both the numbers in the return array must be of the same type. The arithmetic operation is performed on these two numbers, coerced into the same type.
This way, the authors of numeric classes don't have to make their arithmetic operations support operations on
objects of different types. If they implement coerce, they know that their arithmetic operations will only be passed in another object of the same type.
This is easiest to see for the Complex class. Below, every input to coerce is transformed into an equivalent complex number so that it can be used in arithmetic operations along with the complex number i:
require 'complex'
i = Complex(0, 1) # => Complex(0, 1)
i.coerce(3) # => [Complex(3, 0), Complex(0, 1)]
i.coerce(2.5) # => [Complex(2.5, 0), Complex(0, 1)]
This, incidentally, is why 3/4 uses integer division but 3/4.to_f uses floatingpoint division. 3.coerce(4) returns two integer objects, so the arithmetic methods of Fixnum are used. 3.coerce(4.0) returns two floatingpoint numbers, so the arithmetic methods of Float are used.
Other conversion methods
All Ruby objects define conversion methods to_s and inspect, which give a string representation of the object. Usually inspect is the more readable of the two formats.
[1, 2, 3].to_s # => "123"
[1, 2, 3].inspect # => "[1, 2, 3]"
Here's a grab bag of other notable conversion methods found within the Ruby standard library. This should give you a picture of what Ruby conversion methods typically do.
MatchData#to_a creates an array containing the match groups of a regular expression match. Matrix#to_a converts a mathematical matrix into a nested array. Enumerable#to_a iterates over any enumerable object and collects the results in an array. Net::HTTPHeader#to_hash returns a hash mapping the names of HTTP headers to their values. String#to_f and String#to_i parse strings into numeric
objects. Including the bigdecimal/util library will define String#to_d, which parses a string into a BigDecimal object. Including the yaml library will define to_yaml methods for all of Ruby's builtin classes: Array#to_yaml, String#to_yaml, and so on.
See Also
Recipe 1.12, "Testing Whether an Object Is StringLike" Recipe 2.1, "Parsing a Number from a String" Recipe 8.10, "Getting a HumanReadable
Printout of Any Object"
