2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Ruby オブジェクトモデルの鶏と卵パラドックス:BasicObject と Class はどちらが先か

2
Last updated at Posted at 2026-03-20

はじめに

Ruby オブジェクトモデルを理解する(.class / .superclass / ancestors) という記事で、Ruby のオブジェクトモデルを図で整理しました。

                         .class(俺を作ったのは?)
                         ─────────────────────────→
                                                          BasicObject
                                                              ↑ .superclass
                                                            Object
                                                              ↑ .superclass
               BasicObject ────────────.class───→ ┐         Module
                    ↑ .superclass                 │           ↑ .superclass
                    Object ────────────.class───→ ├──→ Class ─┘
                    ↑ .superclass                 │    ↑ │
"hello" ──.class──→ String ────────────.class───→ ┘    └─┘ .class(自分自身!)

  ↑ .superclass(俺の継承元は?)
  • 縦(上向き) = .superclass で継承元を辿る → クラスごとに違う
  • 横(右向き) = .class で「俺を作ったクラス」を辿る → 全員 Class に行き着く

この図を眺めていると、ある矛盾に気づきます。

パラドックスの発見

Ruby のオブジェクトモデルには次の2つの事実があります。

BasicObject.class        # => Class
Class.superclass         # => Module
Module.superclass        # => Object
Object.superclass        # => BasicObject

つまり:

  • BasicObject.class = ClassClass が先に存在している必要がある
  • Class.superclass を辿ると Module → Object → BasicObject に行き着く ← BasicObject が先に存在している必要がある

図にすると:

BasicObject.class = Class         ← Class が先に必要
       ↑
Class.superclass → BasicObject    ← BasicObject が先に必要

どちらが先に存在できるのか?

これは有名な「鶏と卵」問題です。通常の Ruby コードではこの構造を作ることは絶対にできません。

# 通常の Ruby コードでは不可能
class BasicObject; end      # ← この時点で BasicObject.class は何?
class Class < BasicObject; end  # ← Class を定義するには BasicObject が必要

Ruby MRI がどう解決しているか

これは Ruby の C 実装(MRI)がブートストラップ時に2段階で強引に構築することで解決しています。

実際の C ソースコード(ruby/ruby/blob/master/class.c)を見てみましょう。

void
Init_class_hierarchy(void)
{
    // ① まず superclass の繋がりだけ作る(.class の関係は後回し)
    rb_cBasicObject = boot_defclass("BasicObject", 0);
    rb_cObject      = boot_defclass("Object", rb_cBasicObject);
    rb_cModule      = boot_defclass("Module", rb_cObject);
    rb_cClass       = boot_defclass("Class",  rb_cModule);

    // ② 後から .class を全員 Class に向ける
    RBASIC_SET_CLASS(rb_cClass,       rb_cClass);
    RBASIC_SET_CLASS(rb_cModule,      rb_cClass);
    RBASIC_SET_CLASS(rb_cObject,      rb_cClass);
    RBASIC_SET_CLASS(rb_cBasicObject, rb_cClass);
}

ポイント1:boot_defclass とは

boot_defclass は通常の rb_define_class とは異なる特殊な関数です。

通常 Ruby でクラスを定義するには Class がすでに存在している必要があります。しかし boot_defclass は「.class への参照を空にしたまま」オブジェクトを作れるため、Class が存在しない状態でも使えます。

ポイント2:2段階で矛盾を回避

ステップ①: superclass の連鎖だけ構築

BasicObject(.class は空)
    ↑ superclass
  Object(.class は空)
    ↑ superclass
  Module(.class は空)
    ↑ superclass
  Class(.class は空)

ステップ②: 全員の .class を Class に貼り付ける

BasicObject.class = Class ✅
Object.class      = Class ✅
Module.class      = Class ✅
Class.class       = Class ✅(自己参照)

ステップ①では .class の関係をいったん無視して superclass の連鎖だけ作ります。この時点では矛盾が発生しません。

ステップ②で後から .class を全員 Class に向けることで、矛盾なく完成します。

まとめ

Ruby のオブジェクトモデルに見える「鶏と卵」パラドックスは、C ランタイムが2段階でブートストラップすることで解決されています。

ステップ 内容
boot_defclass で superclass の連鎖だけ作る(.class は空のまま)
RBASIC_SET_CLASS で全員の .classClass に向ける

通常の Ruby コードでは絶対に書けない構造を、起動時に C のレベルで無理やり作っています。一言で言えば「Ruby のランタイムが最初に全部同時に作ったから矛盾しない」ということです。


参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?