すがブロ

sugamasaoのhatenablogだよ

Rubyのnilクラスを調べたい

nilクラスが実際にどうなってるのか調べたい

Rubyの実装がどうなっているか、一年に365回くらいは調べたくなると思うのですが、今日はnilクラスについて調べました。

Rubyの組み込みクラスは(多分)リポジトリ直下にそれぞれ、それっぽい名前.cとして存在しているのですが、nilについてはどうやらそうではない模様。

GitHub - ruby/ruby: The Ruby Programming Language

仕方がないので頑張って探してみましょう。

おもむろに git grep nil

rubyリポジトリをgit cloneしてきて*1、おもむろに git grep nil。当たり前だけど普通にnilばっか出てきてダメですね;;

よく考えればnilはクラスではないのでクラス名で検索しないとダメだろう。というわけでNilClassを調べます。念のため書いておきますが、nilのクラスは以下のようにして調べることができますよ。

irb(main):001:0> nil.class
=> NilClass

気を取り直して、おもむろにgit grep NilClass。これでも結構たくさんの結果が出てきてしまいました。ここから目grepするだけでも目的のものはわかるのですが、もうちょっと楽をするために.cのみで検索してみましょう。

% git grep NilClass -- "*.c"
class.c:    SPECIAL_SINGLETON(Qnil, rb_cNilClass);
class.c: *       NilClass, TrueClass and FalseClass.
complex.c:    rb_define_method(rb_cNilClass, "to_c", nilclass_to_c, 0);
ext/json/generator/generator.c:             mTrueClass, mFalseClass, mNilClass, eGeneratorError,
ext/json/generator/generator.c:static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
ext/json/generator/generator.c:    mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
ext/json/generator/generator.c:    rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
object.c:VALUE rb_cNilClass; /*!< NilClass class */
object.c: *  <code>false</code>, it returns NilClass, TrueClass, or FalseClass,
object.c: *     nil.singleton_class         #=> NilClass
object.c: * Document-class: NilClass
object.c:    rb_cNilClass = rb_define_class("NilClass", rb_cObject);
object.c:    rb_define_method(rb_cNilClass, "to_i", nil_to_i, 0);
object.c:    rb_define_method(rb_cNilClass, "to_f", nil_to_f, 0);
object.c:    rb_define_method(rb_cNilClass, "to_s", nil_to_s, 0);
object.c:    rb_define_method(rb_cNilClass, "to_a", nil_to_a, 0);
object.c:    rb_define_method(rb_cNilClass, "to_h", nil_to_h, 0);
object.c:    rb_define_method(rb_cNilClass, "inspect", nil_inspect, 0);
object.c:    rb_define_method(rb_cNilClass, "&", false_and, 1);
object.c:    rb_define_method(rb_cNilClass, "|", false_or, 1);
object.c:    rb_define_method(rb_cNilClass, "^", false_xor, 1);
object.c:    rb_define_method(rb_cNilClass, "===", rb_equal, 1);
object.c:    rb_define_method(rb_cNilClass, "nil?", rb_true, 0);
object.c:    rb_undef_alloc_func(rb_cNilClass);
object.c:    rb_undef_method(CLASS_OF(rb_cNilClass), "new");
rational.c:    rb_define_method(rb_cNilClass, "to_r", nilclass_to_r, 0);
rational.c:    rb_define_method(rb_cNilClass, "rationalize", nilclass_rationalize, -1);
spec/ruby/optional/capi/ext/constants_spec.c:static VALUE constants_spec_rb_cNilClass(VALUE self) {
spec/ruby/optional/capi/ext/constants_spec.c:  return rb_cNilClass;
spec/ruby/optional/capi/ext/constants_spec.c:  rb_define_method(cls, "rb_cNilClass", constants_spec_rb_cNilClass, 0);
sprintf.c:      if (value == rb_cNilClass) {
vm.c:    if (klass == rb_cNilClass) return NIL_REDEFINED_OP_FLAG;
vm.c:          C(NilClass), C(TrueClass), C(FalseClass));

かなり人間がわかるような量になりました。 これで必要な情報はほぼわかったようなものですが、実装はobject.cにあるようです。

object.c 👀

もはや説明不要なレベルですが、先ほどの結果を抜粋すると、object.cのここら辺が見るべきポイントのようですね。

object.c:    rb_cNilClass = rb_define_class("NilClass", rb_cObject);
object.c:    rb_define_method(rb_cNilClass, "to_i", nil_to_i, 0);
object.c:    rb_define_method(rb_cNilClass, "to_f", nil_to_f, 0);
object.c:    rb_define_method(rb_cNilClass, "to_s", nil_to_s, 0);
object.c:    rb_define_method(rb_cNilClass, "to_a", nil_to_a, 0);
object.c:    rb_define_method(rb_cNilClass, "to_h", nil_to_h, 0);
object.c:    rb_define_method(rb_cNilClass, "inspect", nil_inspect, 0);
object.c:    rb_define_method(rb_cNilClass, "&", false_and, 1);
object.c:    rb_define_method(rb_cNilClass, "|", false_or, 1);
object.c:    rb_define_method(rb_cNilClass, "^", false_xor, 1);
object.c:    rb_define_method(rb_cNilClass, "===", rb_equal, 1);
object.c:    rb_define_method(rb_cNilClass, "nil?", rb_true, 0);

あまりCの実装には詳しくありませんが、少なくともrb_define_class でNillClassを定義していて、rb_define_method(rb_cNilClass, ...) でメソッド名と実装の紐付けを行なっていることがわかります。 例えば、to_inil_to_iで実装されているようですね。このnil_to_iも同じobject.cで実装されているのでみてみましょう。

static VALUE
nil_to_i(VALUE obj)
{
    return INT2FIX(0);
}

こんな感じでゼロを返すという強い意志を感じます。

というわけで、NilClassの実装を見ることができました。まあ今回のポイントはどのファイルで実装されているのか?というのを調べる部分で、それはobject.cでした、ということですね。

合わせて読みたい

あんまり関係ないけどこちらもよろしくお願いします

改訂2版 パーフェクトRuby

改訂2版 パーフェクトRuby

*1:実際はruby/rubyghqで手元に取得済みなのであった