Ruby: to_s vs. to_str

I've experienced a bit of confusion when it comes to Ruby's to_s vs. to_str methods. This site says:

to_str is an implicit cast, whereas to_s is an explicit cast. ... If you?re having a hard time remembering which is which, I would remember that there is a reason that to_s is shorter. First, it implies that the object isn?t really much of a string, so we?re only using the first letter ?s?. Also, to_s is shorter because more objects will have to_s methods, so you?ll end up typing it more frequently. With to_str, we?re tagging an object as much closer to being a string, so we give it the first three letters. It?s almost half of a string!

That's about as vague as you can get. It's also a bit confusing because although to_s is supposedly an "explicit class", some built-in methods call it implicitly (e.g. puts).

Programming Ruby, in the Duck Typing chapter (page 372) says:

[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 you object can naturally be used every place a string or an integer could be used.

That's more helpful, but the notion of "strict" vs. "not very strict" certainly isn't very precise. For any given built-in method, does it use to_s or to_str? That's what I'd like to know.

I wrote a test:

# to_s vs. to_str test

module String_Test
    def initialize(arg)
        @test = arg
    end

    def to_s
        puts 'to_s called!'
        'to_s ' + @test
    end

    def to_str
        puts 'to_str called!'
        'to_str ' + @test
    end
end

class Both
    include String_Test
end

class No_to_s
    include String_Test
    undef_method :to_s
end

class No_to_str
    include String_Test
    undef_method :to_str
end

class Neither
    include String_Test
    undef_method :to_s
    undef_method :to_str
end


[Both, No_to_s, No_to_str, Neither].each do |curr_class|
    puts 
    puts '=' * 40
    puts curr_class.to_s
    puts '=' * 40

    t = curr_class.new('foo')
    [
        %q{t                       },
        %q{p t                     },
        %q{print t, "\n"           },
        %q{puts t                  },
        %q{puts t.to_s             },
        %q{puts t, t               },
        %q{"#{t}"                  },
        %q{puts "#{t}"             },
        %q{puts t.to_str           },
        %q{puts 'ADDING:' + t      },
        %q{'123'.split(t)          },
        %q{Dir.glob(t)             },
        %q{File.new(t)             },
        %q{/./.match(t)            },
        %q{nil.respond_to?(t)      },

    ].each do |code|
        puts 'RUNNING: ' + code
        begin
            eval code
        rescue Exception => e
            puts "ERROR: #{e}"
        end
        puts '-' * 20
    end
end

The output (which are long and I won't paste here) shows that:

  • print, puts and string interpolation use to_s. Mostly everything else (glob, split, match, string concatenation) uses to_str.
  • Defining to_str will NOT define to_s for you, and vice versa. Going by the vague intuitive descriptions given above, I'd almost have expected methods that use to_s to know enough to fall back to using to_str. If an object is inherently in some sense a String, why would print/puts output anything BUT the String interpretation of the object? If we're including this to_s / to_str convention in the language, you'd think that an implicit to_s -> to_str fallback would be there too.
  • respond_to? fails. This is because it expects a Symbol. Strings have a to_sym method, but my class doesn't. So even though to_str means your class "can naturally be used every place a string can be used", my class obviously is NOT really a string, since it doesn't inherit all of Strings methods. So there are plenty of places a String "can be used" that my class cannot, for various definitions of "can be used".

So I imagine if you're writing your own class which takes arguments that you plan to coerce into Strings, you should use to_s only if you're outputting or String-interpolating a value, and use to_str if you're doing anything substantial. But this really is a bit vague for my liking.

September 26, 2006 @ 8:18 AM PDT
Cateogory: Programming
Tags: Ruby

5 Comments

Samuel
Quoth Samuel on January 14, 2008 @ 9:06 PM PST

I would look at it this way: to_s is a tool to output human readable status/debug info to_str is expected to be program readable.

Nathan
Quoth Nathan on May 14, 2009 @ 7:48 AM PDT

Heck, just make one an alias for the other.

Omochikaeri
Quoth Omochikaeri on May 26, 2009 @ 9:21 AM PDT

It's naive to define to_s as being human readable. That would imply it has to output the value in the appropriate locale, but 3000.to_s doesn't correctly insert a comma!

Grant Hutchins
Quoth Grant Hutchins on October 30, 2009 @ 2:38 AM PDT

to_s is defined on every object and will always return something.

to_str is only defined on objects that are string-like. For example, Symbol has to_str but Array does not.

Thus, you can use obj.respond_to?(:to_str) instead of something like obj.is_a?(String) if you want to take advantage of duck typing without worrying about whether the class you're working with is a subclass of String or not.

The documentation for Exception#to_str reads: "By supplying a to_str method, exceptions are agreeing to be used where Strings are expected." So it's all about expectations.

  • to_s: Give me a String no matter what!
  • to_str: I am pretty sure you are String-like. So sure, that I'd prefer to get a NoMethodException in the case where you're not.
roger
Quoth roger on January 17, 2012 @ 2:00 AM PST

looks like string interpolation "falls back" on #inspect ?

Speak your Mind

You can use Markdown in your comment.
Email/URL are optional. Email is only used for Gravatar.

Preview