# To\_str behavior

**URL:** <https://www.ruby-forum.com/t/to-str-behavior/111907>\
**Category:** Ruby\
**Created:** [August 13, 2007, 9:02am UTC](https://www.ruby-forum.com/t/to-str-behavior/111907 "2007-08-13T09:02:12Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Nasir\_K](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/838e76/32.png) [@Nasir\_K](https://www.ruby-forum.com/u/Nasir_K)\
**Post date:** [August 13, 2007, 9:02am UTC](https://www.ruby-forum.com/t/to-str-behavior/111907/1 "2007-08-13T09:02:12Z")

</div>

Hi,  
I have two related questions on to\_str behavior. AFAIK to\_str does  
automatic  
coercion to string wherever one is required, it is to be provided for  
objects that exhibit string like behavior. But this mental model did not  
match up with my test -

Consider a simple class

class B  
def initialize(str)  
@value = str  
end  
def to\_s  
@value  
end  
def to\_str  
to\_s  
end  
end

irb(main):038:0\> x = B.new(“hello”)  
=\> #\<B:0x27f1550 @value=“hello”\>

irb(main):042:0\> a = “hello”+x  
=\> “hellohello”

So far so good. Concatenation automatically happend. Then I went on to  
write  
a test -

class MyTest \< Test::Unit::TestCase  
def test\_equality  
x = B.new(“hello”)  
assert\_equal(“hello”, x)  
end  
end

Which failed to my surprise.

1. Failure:  
test\_equality(MyTest) [(irb):49]:  
\<“hello”\> expected but was  
\<#\<B:0x27549bc @value=“hello”\>\>.

1 tests, 1 assertions, 1 failures, 0 errors

Trying to experiment further I made my class B a subclass of String just  
to  
check the test -

class B \< String  
def initialize(str)  
@value = str  
end  
def to\_s  
@value  
end  
def to\_str  
to\_s  
end  
end

irb(main):023:0\* a = B.new(“hello”)  
=\> “”  
irb(main):024:0\>  
irb(main):025:0\*  
irb(main):026:0\* puts a

=\> nil

Which surprised me even more.

Any explanation of these two counter-intuitive behaviors will be greatly  
appeciated.

Thanks  
Nasir

---

<div class="post-metadata">

**Author:** ![Nasir\_K](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/838e76/32.png) [@Nasir\_K](https://www.ruby-forum.com/u/Nasir_K)\
**Post date:** [August 13, 2007, 9:43am UTC](https://www.ruby-forum.com/t/to-str-behavior/111907/2 "2007-08-13T09:43:32Z")

</div>

Nasir K. wrote:

> Hi,  
> Greetings Nasir,

> class MyTest \< Test::Unit::TestCase  
> def test\_equality  
> x = B.new(“hello”)  
> assert\_equal(“hello”, x)  
> end  
> end
> 
> Which failed to my surprise.
> 
> 1. Failure:  
> test\_equality(MyTest) [(irb):49]:  
> \<“hello”\> expected but was  
> \<#\<B:0x27549bc @value=“hello”\>\>.
> 
> 1 tests, 1 assertions, 1 failures, 0 errors  
> Okay, as the results showed, an object “x” of class MyTest does not  
> equal the  
> string results of the method .to\_str called on the x object.

assert\_equal (“hello”, x.to\_str)

is what you want 🙂

> class B \< String  
> def initialize(str)  
> @value = str  
> end  
> def to\_s  
> @value  
> end  
> def to\_str  
> to\_s  
> end  
> end
> 
> irb(main):023:0\* a = B.new(“hello”)  
> =\> “”  
> irb(main):024:0\>  
> irb(main):025:0\*  
> irb(main):026:0\* puts a
> 
> =\> nil
> 
> Which surprised me even more.  
> No surprise here, either. You have added a method, to\_str to the String  
> class,  
> but when you instantiated the Object you did not make any assignment to  
> the string itself.

If you say, for example:

b= String.new  
puts b

you would get the same result, because this is essentially the same  
thing.  
In your a class, a.to\_str should work, I believe, but in my mind, all  
bets are off because you are changing the behavior of the built-in  
String class, which is generally “a bad thing” unless you are very  
careful.

Cheers,

Howard

---

<div class="post-metadata">

**Author:** ![Nasir\_K](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/838e76/32.png) [@Nasir\_K](https://www.ruby-forum.com/u/Nasir_K)\
**Post date:** [August 13, 2007, 1:27pm UTC](https://www.ruby-forum.com/t/to-str-behavior/111907/3 "2007-08-13T13:27:42Z")

</div>

On 8/13/07, Nasir K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Hi,  
> I have two related questions on to\_str behavior. AFAIK to\_str does automatic  
> coercion to string wherever one is required,  
> not quite always, Nasir.  
> Sometime it is rather done by to\_s, look at this example:  
> 519/20 \> irb  
> irb(main):001:0\> x=Object.new  
> =\> #Object:0xb7d71fec  
> irb(main):002:0\> puts x  
> #Object:0xb7d71fec  
> =\> nil  
> irb(main):003:0\> puts x.to\_s  
> #Object:0xb7d71fec  
> =\> nil  
> irb(main):004:0\> x.extend Module::new{ def to\_s; “hi there” end}  
> =\> hi there  
> irb(main):005:0\> puts x  
> hi there  
> =\> nil

it is to be provided for

> ```
> @value
> 
> ```
> 
> =\> “hellohello”  
> yup in this use case you are correct
> 
> Which failed to my surprise.  
> I feel that Howard has explained that nicely 😉  
> check the test -  
> to\_s
> 
> Which surprised me even more.  
> I am not sure if it is Howard who has adressed your surprise here  
> as a matter of fact puts calls to\_s on objects that are not strings,  
> but when they are already they do nothing, they just print the String,  
> however, as Howard has said correctly you just added @value to a  
> String that does not make it have the ‘value’ hello.  
> Try this variation  
> irb(main):019:0\> class B \< String  
> irb(main):020:1\> def initialize s  
> irb(main):021:2\> super s  
> irb(main):022:2\> @value = “value = #{s}”  
> irb(main):023:2\> end  
> irb(main):024:1\> end  
> =\> nil  
> irb(main):025:0\> puts B.new(“hi”)  
> hi  
> =\> nil

you see you get hi and even if you define to\_s that does not change  
the behavior of puts:  
irb(main):026:0\> class B  
irb(main):027:1\> def to\_s  
irb(main):028:2\> @value  
irb(main):029:2\> end  
irb(main):030:1\> end  
=\> nil  
irb(main):031:0\> puts B.new(“hi”)  
hi  
=\> nil  
irb(main):032:0\> puts B.new(“hi”).to\_s  
value = hi  
=\> nil

> Any explanation of these two counter-intuitive behaviors will be greatly  
> appeciated.  
> Hopefully it is less counter-intuitive now, I agree that there might  
> be some slight inconsistencies between to\_str and to\_s, but for the  
> second part, be aware that you extended/subclassed a core class, that  
> is a tricky thing to do sometimes 😉  
> HTH  
> Robert

---

<div class="post-metadata">

**Author:** ![Nasir\_K](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/838e76/32.png) [@Nasir\_K](https://www.ruby-forum.com/u/Nasir_K)\
**Post date:** [August 14, 2007, 5:00am UTC](https://www.ruby-forum.com/t/to-str-behavior/111907/4 "2007-08-14T05:00:48Z")

</div>

Thanks to all the respondents.  
The confusion stemmed from some of the inconsistency that is around the  
to\_str usage within Ruby.

See this below.

class A  
def to\_str  
“hello”  
end  
def to\_s  
“bye”  
end  
end

# Note to\_s and to\_str return different strings.

irb(main):018:0\> “xyz”+A.new  
=\> “xyzhello”

Good, as to\_str is called when it is ‘expected’ that arg to + is going  
to be  
a string.

irb(main):019:0\> “hello” == A.new  
=\> false

This is fine too as David pointed out, one could argue though because  
there  
already is a equal? but anyway…lets move on.

Then see

irb(main):020:0\> A.new.upcase  
NoMethodError: undefined method `upcase’ for hello:A  
from (irb):20

Again since A.new is not a String a method\_missing is called etc.

Bear with me a little more 🙂

irb(main):021:0\> “hellooo”.include? A.new  
=\> true

This is good because the argument is expected to be a string.

Now see this

irb(main):022:0\> “a” \<\< A.new  
=\> “ahello”  
OK so now Ruby used the string representation from to\_str _not_ to\_s  
Now is it true that _only_ object of class String are expected to be  
appended to a string using \<\< operator, hence the coercion?

Not quite -

irb(main):023:0\> “a” \<\< 1  
=\> “a\001”

While of course -  
irb(main):025:0\> 1.to\_str  
NoMethodError: undefined method `to\_str’ for 1:Fixnum

But of course -  
irb(main):038:0\> 1.to\_s  
=\> “1”

So is it the case that when a string is “expected” then a to\_str is  
tried if  
not found then to\_s is tried, as can be deduced from the last two  
operations  
above? But then of course the following would fail -

class B  
def to\_s  
“hello”  
end  
end

irb(main):052:0\* “bye”+B.new  
TypeError: can’t convert B into String  
from (irb):52:in `+’  
from (irb):52  
from :0

Now one could rationalize each behavior in isolation but isnt there an  
inconsistency?

Thanks  
Nasir

---

<div class="post-metadata">

**Author:** ![Nasir\_K](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/838e76/32.png) [@Nasir\_K](https://www.ruby-forum.com/u/Nasir_K)\
**Post date:** [August 14, 2007, 2:38am UTC](https://www.ruby-forum.com/t/to-str-behavior/111907/5 "2007-08-14T02:38:07Z")

</div>

Nasir K. wrote:

> Hi,  
> I have two related questions on to\_str behavior. AFAIK to\_str does  
> automatic  
> coercion to string wherever one is required, it is to be provided for  
> objects that exhibit string like behavior. But this mental model did not  
> match up with my test -

class StringLike  
include Comparable  
def initialize(val)  
@val = val  
end  
def \<=\>(other)  
@val \<=\> other.to\_str  
end  
def to\_str  
@val  
end  
end

x = StringLike.new(“hi”)  
x == “hi” #Â =\> true  
“hi” == x #Â =\> true

That’s from one of my older experiments. I don’t remember the reasons.  
Feel free to examine why it works with \<=\> + Comparable.  
HTH.

Regards  
Stefan

---

<div class="post-metadata">

**Author:** ![Nasir\_K](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/838e76/32.png) [@Nasir\_K](https://www.ruby-forum.com/u/Nasir_K)\
**Post date:** [August 13, 2007, 2:25pm UTC](https://www.ruby-forum.com/t/to-str-behavior/111907/6 "2007-08-13T14:25:53Z")

</div>

Hi –

On Mon, 13 Aug 2007, Nasir K. wrote:

> ```
> @value = str
> 
> ```
> 
> =\> #\<B:0x27f1550 @value=“hello”\>  
> assert\_equal(“hello”, x)  
> 1 tests, 1 assertions, 1 failures, 0 errors  
> You’re making a strange assumption, namely that Ruby cares whether or  
> not your assertion succeeds 🙂 All that’s happening is:

“hello” == x

and String#== does not do any kind of string conversion on its  
operand. It wouldn’t be appropriate, because there’s no reason to  
think you want every == test to pass. That’s different from string  
concatenation, where you would never have any reason to do:

“str” + x

unless the operation was expected to work.

#to\_str is a very “niche” method. It doesn’t morph objects into  
strings; rather, it provides an automatic string representation for  
cases where it makes no sense for anything but a string to be  
involved.

David

---

<div class="post-metadata">

**Author:** ![Nasir\_K](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/838e76/32.png) [@Nasir\_K](https://www.ruby-forum.com/u/Nasir_K)\
**Post date:** [August 14, 2007, 5:25am UTC](https://www.ruby-forum.com/t/to-str-behavior/111907/7 "2007-08-14T05:25:52Z")

</div>

Hi –

On Tue, 14 Aug 2007, Nasir K. wrote:

> def to\_s  
> a string.
> 
> irb(main):019:0\> “hello” == A.new  
> =\> false
> 
> This is fine too as David pointed out, one could argue though because there  
> already is a equal? but anyway…lets move on.

I’m digging around in the source to figure out the == behavior. Here’s  
the method:

static VALUE  
rb\_str\_equal(str1, str2)  
VALUE str1, str2;  
{  
if (str1 == str2) return Qtrue;  
if (TYPE(str2) != T\_STRING) {  
if (!rb\_respond\_to(str2, rb\_intern(“to\_str”))) {  
return Qfalse;  
}  
return rb\_equal(str2, str1);  
}  
if (RSTRING(str1)-\>len == RSTRING(str2)-\>len &&  
rb\_str\_cmp(str1, str2) == 0) {  
return Qtrue;  
}  
return Qfalse;  
}

I haven’t quite figured out what the point is of testing to see  
whether str2 responds to “to\_str”, since even if it does, to\_str isn’t  
called. But I might just have to keep following the trail…

> irb(main):021:0\> “hellooo”.include? A.new  
> =\> true
> 
> This is good because the argument is expected to be a string.

I believe the to\_str thing is always about arguments. The object A.new  
is not just going to act like a string; you can’t call String instance  
methods on it (like upcase). It’s only going to provide its to\_str  
representation when it’s an argument, as in include? .

> irb(main):023:0\> “a” \<\< 1  
> So is it the case that when a string is “expected” then a to\_str is tried if  
> not found then to\_s is tried, as can be deduced from the last two operations  
> above? But then of course the following would fail -

It’s not exactly that. Have a look at this and you’ll see:

irb(main):004:0\> “abc” \<\< 1  
=\> “abc\001”  
irb(main):005:0\> “abc” \<\< 100  
=\> “abcd”  
irb(main):006:0\> “abc” \<\< 10  
=\> “abc\n”

This operation doesn’t involve to\_s or to\_str, so it doesn’t tell you  
anything about how they work. Your next example answers the question,  
though: to\_s does not serve as a fall-back for to\_str.

David

---

<div class="post-metadata">

**Author:** ![Nasir\_K](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/838e76/32.png) [@Nasir\_K](https://www.ruby-forum.com/u/Nasir_K)\
**Post date:** [August 15, 2007, 2:58am UTC](https://www.ruby-forum.com/t/to-str-behavior/111907/8 "2007-08-15T02:58:25Z")

</div>

Hi –

On Wed, 15 Aug 2007, Steven L. wrote:

> > ```
> > return rb_equal(str2, str1);
> > 
> > ```
> > 
> > whether str2 responds to “to\_str”, since even if it does, to\_str isn’t  
> > called. But I might just have to keep following the trail…
> 
> Note the order of arguments. It means str2#== will be used instead of  
> String#==.

Interesting. I’m not sure what the point of the to\_str test is,  
though, since to\_str doesn’t actually seem to get called.

David

---

<div class="post-metadata">

**Author:** ![Nasir\_K](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/838e76/32.png) [@Nasir\_K](https://www.ruby-forum.com/u/Nasir_K)\
**Post date:** [August 15, 2007, 12:32am UTC](https://www.ruby-forum.com/t/to-str-behavior/111907/9 "2007-08-15T00:32:18Z")

</div>

removed\_email\_address@domain.invalid writes:

> ```
> }
> 
> ```
> 
> called. But I might just have to keep following the trail…  
> Note the order of arguments. It means str2#== will be used instead of  
> String#==.

Steve
