概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Implicit vs explicit type conversions in Ruby (to_h/to_hash and others)
- 原文公開日: 2018/01/18
- 著者: zverok — 名サイト「rubyreferences.github.io」の作者でもあります。
Rubyの明示的/暗黙的な型変換についてのメモ(翻訳)
自分用にメモ。
Rubyには型強制(type coercion)メソッドがいくつもペアになっています。
to_i
とto_int
to_s
とto_str
to_a
とto_ary
to_h
とto_hash
右と左はいったい何が違うのでしょうか?どういうときに使い、どんなときに実装するのでしょうか?
短い方は明示的な変換です。
- (integerやstringやarrayといった)何らかの型に変換される可能性がある場合は、その型に明示的な変換を実装すべき
- 何らかの意味のある型に変換されることを期待するのであれば、受け取るデータに対してこれらのメソッドを呼び出すべき
長い方は暗黙的な変換です。
- 型が自分自身を「kind of」に該当する型(よりよい、または特殊なnumber、string、array、hash)に変換されるとみなす場合にのみ、暗黙的な変換を実装すべき
- 変換後の型について厳密な要求がある場合は、受け取るデータに対してこれらのメソッドを呼び出すべきだが、クラスの比較ではなくダックタイピングによるRubyらしい方法でチェックしたいと思うのが普通
- それ以外の場合にこのメソッドを実装したり呼んだりすることはまかりならぬ
Rubyの演算子やコアメソッドは、データを暗黙的に変換します。
"string" + other
は#to_str
onother
を呼び、[1,2,3] + other
は#to_ary
を呼ぶなど(オブジェクトがこのメソッドに応答しない場合はTypeError: no implicit conversion
を返す)"string" * other
や[1,2,3] * other
は#to_int
onother
を呼び出すa, b, c = *other
はother#to_ary
を呼ぶ(そして独自のコレクションオブジェクトを”unpack”するのに使われるが、other
がto_ary
を実装していると思わぬ振る舞いを見せることがある: しかしこれはコレクションではないと考えられる)some_method(*other)
は上と同様にother#to_ary
を使うsome_method(**other)
(Ruby 2で登場)はother#to_hash
を使う
何度でも言います。自分のやっていることを把握できていないのであれば、暗黙の変換を決して実装してはなりません。たとえば#to_hash
が実装されている(#to_h
よりはイケてる名前だから?)ために奇妙極まる振る舞いが発生する事例をあちこちで目にします。
実用的な例をご覧いただきましょう。
class Dog
attr_reader :name, :age
def initialize(name, age)
@name, @age = name, age
end
# シリアライズをto_hではない方法で定義するのは誤り
def to_hash
{species: 'Dog', name: name, age: age}
end
end
def set_options(**some_options)
p some_options
end
dog = Dog.new('Rex', 5)
set_options(foo: 'bar')
# {foo: 'bar'}が出力される、よしよし
set_options(1)
# この呼び方はできない: ありがたいArgumentErrorが発生
set_options(dog)
# なぬ?dogがオプションハッシュとして扱われて
# {species: 'Dog', name: name, age: age}が出力された
require 'awesome_print' # pretty print(pp)に使われる人気gem
ap dog
# のぉぉぉ!Dog#inspectじゃなくてハッシュのppメソッド呼んでるし
以下もご覧ください。