3

Defining to_s on a class works as I expect:

class Foo
  def to_s
    "Joe"
  end 
end

f = Foo.new
"hello #{f}" # => "hello Joe"

I attempt to utilize to_i and expect it to work in a similar fashion. I expect that, by defining to_i to be a number, then anywhere a number is expected, the object will return that number in the place of the object; in a situation like the following, I expect it to return the integer 5. Instead, it raises an error:

class Foo
  def to_i
    0
  end
end

f = Foo.new
5 + f # => TypeError: Foo can't be coerced into Fixnum

What does defining to_i enable? How do you utilize it? Can I implicitly represent this object as an integer and return 0 just like the object implicitly returns the string "Joe"?

sawa
  • 165,429
  • 45
  • 277
  • 381
Neil
  • 4,578
  • 14
  • 70
  • 155
  • 2
    This will work if you implement `to_int`, see http://stackoverflow.com/questions/11182052/to-s-vs-to-str-and-to-i-to-a-to-h-vs-to-int-to-ary-to-hash-in-ruby – matt Oct 12 '15 at 01:57
  • @matt would you mind elaborating a bit more? I did this: `def to_int; 0; end` and it errored out just the same as the `to_i` definition above. – Neil Oct 12 '15 at 02:04
  • 1
    @matt: No, it's `coerce`: `class Foo; def coerce(n) [0, n] end end`. – cremno Oct 12 '15 at 03:48
  • @cremno Yes, you’re right, sorry. `to_int` will work for other things like using it as an array index, but you need `coerce` for arithmetic. – matt Oct 12 '15 at 12:55

1 Answers1

2

The crucial difference between the two cases is that string interpolation "#{}" calls to_s implicitly while Fixnum#+ does not call to_i implicitly on its argument.

Defining to_i only lets you call it (explicitly). Whether or not it is called implicitly depends on where that is used. There is nothing you can do to make to_i be called implicitly just by defining to_i. If you really want to do it, you have to modify Fixnum#+.

sawa
  • 165,429
  • 45
  • 277
  • 381