Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

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

概要

原著者の許諾を得て翻訳・公開いたします。

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

自分用にメモ。

Rubyには型強制(type coercion)メソッドがいくつもペアになっています。

  • to_ito_int
  • to_sto_str
  • to_ato_ary
  • to_hto_hash

右と左はいったい何が違うのでしょうか?どういうときに使い、どんなときに実装するのでしょうか?

短い方は明示的な変換です。

  • (integerやstringやarrayといった)何らかの型に変換される可能性がある場合は、その型に明示的な変換を実装すべき
  • 何らかの意味のある型に変換されることを期待するのであれば、受け取るデータに対してこれらのメソッドを呼び出すべき

長い方は暗黙的な変換です。

  • 型が自分自身を「kind of」に該当する型(よりよい、または特殊なnumber、string、array、hash)に変換されるとみなす場合にのみ、暗黙的な変換を実装すべき
  • 変換後の型について厳密な要求がある場合は、受け取るデータに対してこれらのメソッドを呼び出すべきだが、クラスの比較ではなくダックタイピングによるRubyらしい方法でチェックしたいと思うのが普通
  • それ以外の場合にこのメソッドを実装したり呼んだりすることはまかりならぬ

Rubyの演算子やコアメソッドは、データを暗黙的に変換します。

  • "string" + otherother#to_strを呼び、[1,2,3] + other#to_aryを呼ぶなど(オブジェクトがこのメソッドに応答しない場合はTypeError: no implicit conversionを返す)
  • "string" * other[1,2,3] * otherother#to_intを呼び出す
  • a, b, c = *otherother#to_aryを呼ぶ(そして独自のコレクションオブジェクトを"unpack"するのに使われるが、otherto_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メソッド呼んでるし

以下もご覧ください。

関連記事

Ruby: 自分のクラスに`#to_a`を実装すべきじゃない(翻訳)

Ruby 2.5の`yield_self`が想像以上に何だかスゴい件について(翻訳)

Ruby: 「マジック」と呼ぶのをやめよう(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。