暗黙の型変換
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 って動的型付け言語だから、引数の型が○○でないと……っていう場面がなかなか思いつかない。
結論
- やっぱり、それほど暗黙ではやってくれない。
- 暗黙の型変換は、あんまり必要ない。