Ruby: .+ 方法在内部是如何工作的?

Ruby: How ist the .+ method working internally?

假设我创建了一个对象 a 并给它一个方法。to_i,为什么这个对象不能添加到一个 Integer 中?

>> a = Object.new
=> #<Object:0x0000000006cfa9d0>
?> def a.to_int
?>   42
>> end
=> :to_int
>> 3 + a
(irb):5:in `+': Object can't be coerced into Integer (TypeError)
        from (irb):5:in `<main>'

感谢 Stefan,它成功了!

irb(main):010:1* def a.coerce other
irb(main):011:1*   [other, 42]
irb(main):012:0> end
=> :coerce
irb(main):013:0> 1 + a
=> 43

你仍然需要调用 to_int 方法解释器怎么知道你想做什么?

>> 3 + a.to_int

Ruby 不进行自动转换。

>> 3 + "5" 

这会产生同样的错误,即使“5”有一个非常好的 to_i 方法。顺便提一句。 ruby 中的 to int 方法通常称为 to_i 以防你想保持一致性。

可以通过实现 + 来实现向对象添加整数,例如:

class Foo
  def initialize(value)
    @value = value
  end

  def to_i
    @value
  end

  def +(other)
    Foo.new(to_i + other.to_i)
  end
end

Foo.new(5) + 4
#=> #<Foo:0x00007fbd22050640 @value=9>

为了将 Foo 的实例添加到一个整数,您还必须实现 coerce 它将左侧值作为参数, returns 一个数组两个值都转换为 Foo 个实例,例如:

class Foo
  # ...

  def coerce(other)
    [Foo.new(other.to_i), self]
  end
end

这给你:

4 + Foo.new(5)
#=> #<Foo:0x00007fba600e3e28 @value=9>

Numeric 的文档包含另一个示例。

在内部,如果参数不是整数,coerceInteger#+ 调用:(C 代码)

VALUE
rb_int_plus(VALUE x, VALUE y)
{
    if (FIXNUM_P(x)) {
        return fix_plus(x, y);
    }
    else if (RB_TYPE_P(x, T_BIGNUM)) {
        return rb_big_plus(x, y);
    }
    return rb_num_coerce_bin(x, y, '+');
}

rb_num_coerce_bin 调用 coerce,然后对返回值调用二元运算符 +

在 Ruby 中,这将是:(简化)

class Integer
  def +(other)
    if other.is_a?(Integer)
      # ...
    else
      x, y = other.coerce(self)
      x + y
    end
  end
end