Rubyの明示的/暗黙的な型変換についてのメモ(翻訳)

概要 原著者の許諾を得て翻訳・公開いたします。 英語記事: 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 on otherを呼び、[1,2,3] + otherは#to_aryを呼ぶなど(オブジェクトがこのメソッドに応答しない場合はTypeError: no implicit conversionを返す) “string” * otherや[1,2,3] * otherは#to_int on otherを呼び出す 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メソッド呼んでるし 以下もご覧ください。 to_i Vs to_int Ruby: to_s vs. to_str StackOverflow discussion 関連記事 Ruby: 自分のクラスに`#to_a`を実装すべきじゃない(翻訳) Ruby 2.5の`yield_self`が想像以上に何だかスゴい件について(翻訳) Ruby: 「マジック」と呼ぶのをやめよう(翻訳)