Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

to_s vs. to_str (and to_i/to_a/to_h vs. to_int/to_ary/to_hash) in Ruby

I'm learning Ruby and I've seen a couple of methods that are confusing me a bit, particularly to_s vs to_str (and similarly, to_i/to_int, to_a/to_ary, & to_h/to_hash). What I've read explains that the shorter form (e.g. to_s) are for explicit conversions while the longer form are for implicit conversions.

I don't really understand how to_str would actually be used. Would something other than a String ever define to_str? Can you give a practical application for this method?

like image 583
Jeff Storey Avatar asked Jun 24 '12 23:06

Jeff Storey


People also ask

What is TO_S method in Ruby?

The to_s function in Ruby returns a string containing the place-value representation of int with radix base (between 2 and 36). If no base is provided in the parameter then it assumes the base to be 10 and returns.

What is TO_I in Ruby?

The to_i function in Ruby converts the value of the number to int. If the number itself is an int, it returns self only. Parameter: The function takes the number which is to be converted to int. Return Value: The function returns the int value.


2 Answers

Note first that all of this applies to each pair of “short” (e.g. to_s/to_i/to_a/to_h) vs. “long” (e.g. to_str/to_int/to_ary/to_hash) coercion methods in Ruby (for their respective types) as they all have the same semantics.


They have different meanings. You should not implement to_str unless your object acts like a string, rather than just being representable by a string. The only core class that implements to_str is String itself.

From Programming Ruby (quoted from this blog post, which is worth reading all of):

[to_i and to_s] are not particularly strict: if an object has some kind of decent representation as a string, for example, it will probably have a to_s method… [to_int and to_str] are strict conversion functions: you implement them only if [your] object can naturally be used every place a string or an integer could be used.

Older Ruby documentation from the Pickaxe has this to say:

Unlike to_s, which is supported by almost all classes, to_str is normally implemented only by those classes that act like strings.

For example, in addition to Integer, both Float & Numeric implement to_int (to_i's equivalent of to_str) because both of them can readily substituted for an Integer (they are all actually numbers). Unless your class has a similarly tight relationship with String, you should not implement to_str.

like image 153
Andrew Marshall Avatar answered Oct 10 '22 07:10

Andrew Marshall


To understand if you should use/implement to_s/to_str, let's look at some exemples. It is revealing to consider when these method fail.

1.to_s              # returns "1" Object.new.to_s     # returns "#<Object:0x4932990>" 1.to_str            # raises NoMethodError Object.new.to_str   # raises NoMethodError 

As we can see, to_s is happy to turn any object into a string. On the other hand, to_str raises an error when its parameter does not look like a string.


Now let us look at Array#join.

[1,2].join(',')     # returns "1,2" [1,2].join(3)       # fails, the argument does not look like a valid separator. 

It is useful that Array#join converts to string the items in the array (whatever they really are) before joining them, so Array#join calls to_s on them.

However, the separator is supposed to be a string -- someone calling [1,2].join(3) is likely to be making a mistake. This is why Array#join calls to_str on the separator.


The same principle seems to hold for the other methods. Consider to_a/to_ary on a hash:

{1,2}.to_a      # returns [[1, 2]], an array that describes the hash {1,2}.to_ary    # fails, because a hash is not really an array. 

In summary, here is how I see it:

  • call to_s to get a string that describes the object.
  • call to_str to verify that an object really acts like a string.
  • implement to_s when you can build a string that describes your object.
  • implement to_str when your object can fully behave like a string.

I think a case when you could implement to_str yourself is maybe a ColoredString class -- a string that has a color attached to it. If it seems clear to you that passing a colored comma to join is not a mistake and should result in "1,2" (even though that string would not be colored), then do implement to_str on ColoredString.

like image 23
Eldritch Conundrum Avatar answered Oct 10 '22 07:10

Eldritch Conundrum