ruby – 为什么`Kernel :: String`检查`to_str`结果而`Kernel :: Integer`不检查`to_int`结果?

Kernel :: Integer和Kernel :: String都通过首先尝试调用“long”方法(分别为to_int和to_str),然后是“short”方法(分别为to_i和to_str)来转换参数.两种方法都检查“短”方法结果的类,并在需要时引发错误:

[1] pry(main)> class Dummy
[1] pry(main)*   def to_i
[1] pry(main)*     "42"
[1] pry(main)*   end
[1] pry(main)*   def to_s
[1] pry(main)*     42
[1] pry(main)*   end
[1] pry(main)* end;
[2] pry(main)> Integer(Dummy.new)
TypeError: can't convert Dummy to Integer (Dummy#to_i gives String)
from (pry):9:in `Integer'
[3] pry(main)> String(Dummy.new)
TypeError: can't convert Dummy to String (Dummy#to_s gives Fixnum)

这种行为似乎是合乎逻辑的,因为“短”方法应该简单地给出“表示”.另一方面,只有当所讨论的对象本质上是整数或字符串(see this answer)时,才应该实现“长”方法.

但是,一旦我们实现“长”方法,行为就会变得不一致:

[4] pry(main)> class Dummy
[4] pry(main)*   def to_int
[4] pry(main)*     "42"
[4] pry(main)*   end
[4] pry(main)*   def to_str
[4] pry(main)*     42
[4] pry(main)*   end
[4] pry(main)* end;
[5] pry(main)> Integer(Dummy.new)
=> "42"
[6] pry(main)> String(Dummy.new)
TypeError: can't convert Dummy to String (Dummy#to_str gives Fixnum)

为什么结果处理方式不同?

我正在使用ruby 2.1.2,顺便说一下:

[7] pry(main)> RUBY_VERSION
=> "2.1.2"

最佳答案 当你调用Kernel#Integer,又名rb_f_integer时会发生以下情况:

> rb_f_integer调用rb_convert_to_integer
> rb_convert_to_integer调用convert_type(val,“Integer”,“to_int”,FALSE)
> convert_type返回val.to_int,无论它是否实际上是一个整数.
> rb_convert_to_integer的重要部分是:

tmp = convert_type(val, "Integer", "to_int", FALSE);
if (NIL_P(tmp)) { // checks if val.to_int is nil. this is the line that causes this
    return rb_to_integer(val, "to_i");
}
return tmp;

因此,它检查to_int的返回值以查看它是否为nil,而不是它是否为整数.我在该代码中评论的行是导致此错误的行.在上面的rb_to_integer调用中检查to_i的结果类型(如果to_int的结果为nil或未定义to_int),则永远不会检查to_int的结果类型.
这个有趣的结果:

class X
  def to_int
    nil
  end

  def to_i
    42
  end
end

class Y
  def to_int
    false
  end

  def to_i
    42
  end
end

p Integer(X.new) #=> 42
p Integer(Y.new) #=> false
点赞