暗黙の型変換

Ruby は暗黙の型変換をしてくれない気の利かない言語だと言われるが、なかなかどうして結構型変換してくれる。

例えばこんな感じ。

"%d" % "0xff"   # => "255"  # 16進数を 10進数に変換して再文字列化
"%s" % 0        # => "0"    # C 言語なら SEGV もの

試しにオレオレクラスに変換メソッドを思いつくだけ書いてみる。

class Foo
  def to_s; "to_s" end
  def to_i; 1 end
  def to_f; 0.2 end
  def to_a; [:to_a] end
  def to_c; Complex::I end
  def to_r; Rational(1,2) end

  def to_str; "to_str" end
  def to_int; 10 end
  def to_ary; [:to, :ary] end
  def to_hash; {to: :hash} end
  def to_regexp; /[to_regexp]/ end

  def to_sym; :to_sym end
end

値は適当。どの変換メソッドが呼ばれたか分かれば、なんでもいい。

さっそく変換させてみる。

foo = Foo.new

"%d" % foo          # => can't convert Symbol into Integer (TypeError)
"%f" % foo          # => can't convert Symbol into Float (TypeError)
"%s" % foo          # => "to" # !> too many arguments for format string
"%s %s" % foo       # => "to ary"

見事に惨敗。

本当に変換メソッドが機能しているのか?
ユーザが明示的に使う to_. はいいとして to_.* の動きを確認したい。

Integer(foo)        # => 10
Float(foo)          # => 0.2
String(foo)         # => "to_s"    # え? to_str じゃなくて?
Array(foo)          # => [:to, :ary]

String.try_convert(foo)     # => "to_str"
Array.try_convert(foo)      # => [:to, :ary]
Hash.try_convert(foo)       # => {:to=>:hash}
Regexp.try_convert(foo)     # => /[to_regexp]/

0.coerce foo                # => [0.2, 0.0]    # なぜ to_f ?
Complex::I.coerce foo       # => Foo can't be coerced into Complex (TypeError)
Rational(2,1).coerce foo    # => Foo can't be coerced into Rational (TypeError)

なんか変な感じだが、暗黙の型変換をしてくれるか確認。

"#{foo}"                # => "to_s"
'' + foo                # => "to_str"
[] + foo                # => [:to, :ary]
"ruby" =~ foo           # => nil    # ダメポ
Regexp.union(//, foo)   # => /(?-mix:)|(?-mix:[to_regexp])/

foo.to_s == foo         # => false
foo.to_i == foo         # => false
foo.to_f == foo         # => false
foo.to_a == foo         # => false
foo.to_c == foo         # => false
foo.to_r == foo         # => false

foo.to_str == foo       # => false
foo.to_int == foo       # => false
foo.to_ary == foo       # => false
foo.to_hash == foo      # => false
foo.to_regexp == foo    # => false

うぅ〜む。Ruby って動的型付け言語だから、引数の型が○○でないと……っていう場面がなかなか思いつかない。

結論

  • やっぱり、それほど暗黙ではやってくれない。
  • 暗黙の型変換は、あんまり必要ない。