Explaination of Conversion protocols: why to_a and to_ary?

[Updated]

Help me understand this subject.

I’m reading the book “Programming Ruby 1.9 & 2.0” by Dave Thomas, and the Chapter “Standard Protocols and Coercion” explain it but I didn’t understand it.

Why are there 2 versions of conversion to the same datatype?

Why are there to_a and to_ary instead of only one of those? or to_s and to_str?

As an example, you can see in the following code how depending of the context, sometimes Ruby uses to_a and some times to_ary.

class A
  def to_ary
    [1, 2]
  end
  def to_a
    [3, 4]
  end
end

o = A.new
a, b = o
puts a, b # outputs 1, 2

a, b = *o
puts a, b # outputs 3, 4

I think you are highlighting that the splat operator can be used with things that are not arrays, and for those objects an equivalent array must be made by calling to_a.

For example, if you replace o with a range, you need the splat to get the multiple assignment of elements, because only then is the range converted to an array:

2.7.1 :022 > a, b = (1..2)
2.7.1 :023 > a
 => 1..2 
2.7.1 :024 > b
 => nil 
2.7.1 :025 > a, b = *(1..2)
2.7.1 :026 > a
 => 1 
2.7.1 :027 > b
 => 2 

to_ary and to_a should return the same thing for an array (self). You have made the two methods return different things, which highlights how splat works: class Array - RDoc Documentation

Thank you.

I’ll update the question to direct the answers more to clarify what I’m not understanding. Check it out.

Why are there 2 versions of conversion to the same datatype?

I think the intentions are different. to_a is a real conversion, and forces an array to be returned; it exists on several classes (e.g. through Enumerable). to_ary is for classes that are ‘array-like’ enough to be treated as arrays in themselves.

For example, the range (1…2) does not respond to to_ary, and hence is not array-like enough to be split for the multiple assignment. However, the range does respond to to_a, and then it is converted into an array.

So if you were writing a new class, you might want to implement to_a, because it makes sense to convert your instance into an array. But you would only implement to_ary if, in some sense, your class is a kind of array.

For example, Time has a to_a method, because it makes sense to return its data in an array form. But it does not have a to_ary method, because it is not, in itself, array-like.

(to_ary feels like a type check to me: does this instance respond to to_ary? If so, then it’s an ‘array-like’ object.)

2 Likes