为什么 `Kernel::String` 检查 `to_str` 结果而 `Kernel::Integer` 不检查 `to_int` 结果?
Why does `Kernel::String` check `to_str` result while `Kernel::Integer` doesn't check `to_int` result?
Kernel::Integer
和 Kernel::String
都通过首先尝试调用 "long" 方法(分别为 to_int
和 to_str
)来转换参数,然后 "short" 方法(分别为 to_i
和 to_str
)。两种方法都检查 "short" 方法结果的 class 并在需要时引发错误:
[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)
这种行为似乎是合乎逻辑的,因为 "short" 方法应该简单地给出 "representation"。另一方面,只有当所讨论的对象本质上是整数或字符串 (see this answer) 时,才应该实施 "long" 方法。
但是,一旦我们实现了 "long" 方法,行为就会变得不一致:
[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
returns 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
的 return 值以查看它是否为零,而不是它是否为整数。我在该代码中注释的行是导致此错误的行。 to_i
的结果类型在上面对 rb_to_integer
的调用中被检查(如果 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
Kernel::Integer
和 Kernel::String
都通过首先尝试调用 "long" 方法(分别为 to_int
和 to_str
)来转换参数,然后 "short" 方法(分别为 to_i
和 to_str
)。两种方法都检查 "short" 方法结果的 class 并在需要时引发错误:
[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)
这种行为似乎是合乎逻辑的,因为 "short" 方法应该简单地给出 "representation"。另一方面,只有当所讨论的对象本质上是整数或字符串 (see this answer) 时,才应该实施 "long" 方法。
但是,一旦我们实现了 "long" 方法,行为就会变得不一致:
[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
returnsval.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
的 return 值以查看它是否为零,而不是它是否为整数。我在该代码中注释的行是导致此错误的行。 to_i
的结果类型在上面对 rb_to_integer
的调用中被检查(如果 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