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?
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.
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.
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
andto_s
] are not particularly strict: if an object has some kind of decent representation as a string, for example, it will probably have ato_s
method… [to_int
andto_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
.
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:
to_s
to get a string that describes the object.to_str
to verify that an object really acts like a string.to_s
when you can build a string that describes your object.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With