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_s
and 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_s
vs to_str
, but the logic applies as well to to_int
, to_ary
, to_hash
, to_regexp
and to_sym
.
Difference In Scope Of Definition
First of, 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:
This returns:
#<Demo:0x007fc49b05a408>
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_s
returns a string representation of an objectto_str
is actually stating that the object behaves like a string!
String Representation Of An Object
When calling 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
Calling to_str
should return a string-like object, behaving just like a String
.
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 to_str
is String
:
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:
We get '+': no implicit conversion of Fixnum into String (TypeError)
. This makes sense, but what would happen if we were to define to_str
on the Fixnum
class?
The when we call:
We get "15042"
, which is quite surprising! It’s because 42
was implicitly converted to a String
.
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_s
is 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_s
andto_str
are very different.to_s
is defined on most objects and returns a string representation of this object.- Defining
to_str
on an object is very much like saying “this object behaves like a string”. - Calling
to_str
should return a string-like object, not juste a representation of the object as a string.
Note that both to_s
and to_str
should return an instance of String
, even when subclassing String
.
Since you scrolled this far, you might be interested in some other things I wrote:
- Add Text Over an Image with Ruby
- Misuse of update/update! in Ruby on Rails
- Regression Testing For Data
- Expressing Intent Without Comments In Ruby
- Understanding Rails' Forgery Protection Strategies
- First Impressions: Rails 5 on Google App Engine
- Automatically Run RSpec on Multiple Projects
- Startup & Tech Book Reviews