Techioz Blog

Ruby では、inspect() が object_id() が与えるものとは異なるある種のオブジェクト ID を出力するのはなぜですか?

概要

p 関数を使用してオブジェクトを出力すると、ID が与えられる場合がありますが、それは object_id() が与えるものとは異なります。数値が異なる理由は何ですか?

更新: 0x4684abc は 36971870 (0x234255E) とは異なります

>> a = Point.new
=> #<Point:0x4684abc>

>> a.object_id
=> 36971870

>> a.__id__
=> 36971870

>> "%X" % a.object_id
=> "234255E"

解決策

Inspection のデフォルト実装は to_s のデフォルト実装を呼び出します。これは、Object#to_s ドキュメントにあるように、オブジェクトの 16 進値を直接表示するだけです (メソッドの説明をクリックするとソースが表示されます)。

一方、object_id の実装の基礎となる C ソースのコメントは、オブジェクトのタイプに応じて、Ruby 値とオブジェクト ID に異なる「名前空間」があることを示しています (たとえば、Fixnum を除くすべての最下位ビットはゼロのようです)。これは Object#object_id ドキュメントで確認できます (クリックするとソースが表示されます)。

そこから、「オブジェクト ID 空間」 (object_id によって返される) ではオブジェクトの ID が右の 2 番目のビット (最初のビットは 0) から始まりますが、「値空間」 (inspect によって使用される) ではであることがわかります。右側の 3 番目のビットから始まります (最初の 2 ビットはゼロです)。したがって、値を「オブジェクト ID 空間」から「値空間」に変換するには、object_id を 1 ビット左にシフトし、inspect で表示されるのと同じ結果を得ることができます。

> '%x' % (36971870 << 1)
=> "4684abc"

> a = Foo.new
=> #<Foo:0x5cfe4>
> '%x' % (a.object_id << 1)
=> "5cfe4"

注: object_id に関する詳細は、当時 (2010 年) には正確でしたが、新しい Ruby バージョンで object_id がメモリ アドレスから分離された後は、正確ではなくなりました。答えは「object_id がオンデマンドで生成されるから」ということになります。コメントを参照してください。