If you ever looked at the available methods on some objects in Ruby, you might have noticed that there seems to be two different ways to cast an object to string:
to_str. However, most people only use
to_s… is it because it’s shorter or is
to_str functionally different?
Short answer: they are indeed different.
Note that this article focuses on
to_str, but the logic applies as well to
Difference In Scope Of Definition
to_s is defined on a lot of elements. To demonstrate this, let’s create a new class
Demo and call
to_s on a new instance of this class:
We could also call
to_s directly on the class and get a valid result as well since the
Class class is also an object:
> Class.to_s => "Class" > Class.new.to_s => "#<Class:0x007f8f5c02f5b0>"
However if we try to do the same with
to_str, it won’t work because it’s not defined on a higher level class:
This will return
undefined method to_str for #<Demo:0x007fea8204e290> (NoMethodError), meaning that we have to define it ourselves when creating a new class.
Difference In Behavior
Alright, so the methods are not defined in the same way… but this is just a minor detail compared to the main difference between the two:
to_sreturns a string representation of an object
to_stris actually stating that the object behaves like a string!
String Representation Of An Object
to_s, it will return some form of string representation of the object. Something easy to display.
When creating a new class you can keep the default behaviour or build your own. For instance here’s how
to_s behaves on an integer:
Here’s what it could look like if you were defining it yourself:
It’s basically just a way to have a quick and nice way to display your objects that is going to be called when needed, for instance when using
puts or when interpolating with
Behaving Like A String
to_str should return a string-like object, behaving just like a
On the other hand when an object implements
to_str, it has way more consequences as it means that it will return something that behaves like a string. It’s basically saying that the class is not necessarily a
String, but it can be used in the same way.
Because of this, the only class in Ruby core implementing
Exception used to implement
to_str as well but is was removed in Ruby 1.9, which is why it’s often mentioned as an example. The Ruby documentation was even wrong at the time of writing this article, so I wrote a PR to fix it that was merged.
There are a lot of discussions regarding if a class should implement
to_str or not, since it’s a strong signal that the class is really similar to a string and should behave as such. If this sounds interesting, you should take a look at this Symbol#to_str discussion on the Ruby core tracker, or at this example in Rails of when
to_str is useful by Aaron Patterson.
Example 1: Fixnum
Fixnum does not define
to_str, so when we do this:
'+': no implicit conversion of Fixnum into String (TypeError). This makes sense, but what would happen if we were to define
to_str on the
The when we call:
"15042", which is quite surprising! It’s because
42 was implicitly converted to a
Example 2: User
Going back to our other example, we could add
to_str to the
User class and get the ability to use
+ to concatenate an instance of
User with a string:
Note On Implicit / Explicit Conversion
We can also say that
to_s is an explicit conversion and
to_str is an implicit conversion. I’m not going into the details of this, if you’d like to get more information I recommend reading Confident Ruby.
Quoting directly from the book:
to_sis an explicit conversion method. Explicit conversions represent conversions from classes which are mostly or entirely unrelated to the target class.
to_str, on the other hand, is an implicit conversion method. Implicit conversions represent conversions from a class that is closely related to the target class.
Summing It Up
That’s a lot of information, but let me sum it up quickly before finishing the article:
to_strare very different.
to_sis defined on most objects and returns a string representation of this object.
to_stron an object is very much like saying “this object behaves like a string”.
to_strshould return a string-like object, not juste a representation of the object as a string.
Note that both
to_str should return an instance of
String, even when subclassing