Ruby 3.2 - Class

Ruby 3.2 アドベントカレンダーの9日目の記事です。

qiita.com


Class

Class#attached_object 追加

Feature #12084: Class#instance - Ruby master - Ruby Issue Tracking System

Ruby では、オブジェクトのクラスには存在しないメソッドをオブジェクト専用に定義できる。

class Hoge
end

hoge1 = Hoge.new
hoge2 = Hoge.new

def hoge1.fuga
end

hoge1.fuga
hoge2.fuga  #=> undefined method `fuga' for #<Hoge:0x00007f4174ff6830> (NoMethodError)

上の例では Hoge クラスのオブジェクト hoge1 には fuga があるけど、同じ Hoge クラスのオブジェクトの hoge2 には fuga が無いのでエラーとなる。

普通はメソッドはクラスに定義されるものなんだけど、fugahoge1オブジェクトに直接定義されてるように見える。 けど、実は内部的には各オブジェクトには無名のクラスがあってそこにメソッドが定義されてる。 そのクラスを特異クラスといって、singleton_class で取り出すことができる。

hoge1_s_c = hoge1.singleton_class  #=> #<Class:#<Hoge:0x00007ff418c969e0>>
hoge2_s_c = hoge2.singleton_class  #=> #<Class:#<Hoge:0x00007ff418c96968>>

名前がついてなのでわかりにくいけど、hoge1_s_choge2_s_c の値が違うので別のものだとわかる。 表示結果から Hoge クラスのオブジェクトについてる特異クラスであることがわかる。

そのクラスに定義されてるメソッドの中に fuga があるのがわかる。

hoge1_s_c.instance_methods.include?(:fuga)  #=> true
hoge2_s_c.instance_methods.include?(:fuga)  #=> false

で、Ruby 3.1 まではこの特異クラスに対応するオブジェクトは Ruby 的に綺麗に取り出す方法がなかった。

Ruby 3.2 では attached_object というメソッドで取り出すことができるようになった。

hoge1_s_c.attached_object  #=> #<Hoge:0x00007ff418c969e0>
hoge1_s_c.attached_object == hoge1  #=> true
hoge2_s_c.attached_object  #=> #<Hoge:0x00007ff418c96968>
hoge2_s_c.attached_object == hoge2  #=> true

使いどころは自分はよくわかってない。

なお、特異クラスではないクラスで attached_object を呼ぶとエラーになる。

Hoge.attached_object  #=> `Hoge' is not a singleton class (TypeError)