Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Ruby MRIのソースコードを読んで内部構造を学ぶ方法

Rubyを使い込んでいると、その裏側でどのように動作しているのか気になったことはありませんか?

Rubyの内部に深く踏み込む方法の一つが、それを実現しているソースコードを読むことです。C言語がわからなくても、興味深い発見はたくさんあります。

ソースコードはRubyのGitHubリポジトリで公開されています。

理想を言えば、クラス名やメソッド名を簡単に検索できるCodequeryのようなツールを使うのがおすすめです。

コアクラスを探検する

調査のほとんどはルートフォルダで行うことになります。ここには、object.cにあるObjectや、array.cにあるArrayなど、すべてのコアクラスのソースコードが格納されています。

では、hash.cを見てみましょう。

4468行目までスクロールすると、見覚えのある名前が並んでいるはずです。

まずはこの行から始めましょう:

rb_cHash = rb_define_class("Hash", rb_cObject);

この行では、Hashクラスがrb_define_class関数によって定義されています。第2引数(rb_cObject)はこのクラスのスーパークラスです。

クラス定義のプロセスがどのように動作するのか知りたい場合は、rb_define_classを検索してみましょう。

Ruby MRIのソースコードを読んで内部構造を学ぶ方法

rb_define_classの最初の部分では、クラスがすでに定義されているかどうかをチェックしています。

if (rb_const_defined(rb_cObject, id)) {
  // ...
}

このifブロックの中で、Rubyは「すでに定義されているものが本当にクラスであること」を確認するなどのサニティチェックを行います。

クラスがまだ定義されていない場合は、次のように定義されます:

klass = rb_define_class_id(id, super);

st_add_direct(rb_class_tbl, id, klass);
rb_name_class(klass, id);
rb_const_set(rb_cObject, id, klass);
rb_class_inherited(super, klass);

return klass;

これらすべての関数の定義を読むこともできますが、かなり直感的で理解しやすい内容だと思います。

st_add_directの「st」は「symbol table(シンボルテーブル)」を意味しており、これは単なるハッシュテーブルです。rb_const_set関数はObjectクラスに定数を設定します。これにより、そのクラスはどこからでも利用できるようになります。

そして、rb_class_inheritedはスーパークラスのinheritedメソッドを呼び出します。このメソッドのドキュメントはRuby公式サイトで確認できます。

コードの次のセクションはメソッド定義で構成されています。MRIでは、これらをrb_define_methodで定義しています。

以下はその例です:

rb_define_method(rb_cHash,"index", rb_hash_index, 1);
rb_define_method(rb_cHash,"size", rb_hash_size, 0);
rb_define_method(rb_cHash,"length", rb_hash_size, 0);
rb_define_method(rb_cHash,"empty?", rb_hash_empty_p, 0);

引数は次のような構成になっています:

  • 第1引数: メソッドを定義する対象のクラス
  • 第2引数: メソッド名。第3引数は、そのメソッドを実際に実装しているC言語の関数
  • 最後の引数: このRubyメソッドが必要とする引数の数(負の値は省略可能な引数を意味します)

rb_define_singleton_method関数は、クラスメソッドを定義するために使われます。

rb_define_singleton_method(rb_cHash, "[]", rb_hash_s_create, -1);

rb_define_singleton_methodの中身は、実はたった1行のコードです:

rb_define_method(singleton_class_of(obj), name, func, argc);

さらに探検を続けたい場合は、object.cが良い候補になるでしょう。

標準ライブラリを探検する

さて、今日のところはC言語はこのくらいにしましょう!

今度はRubyのコードを読んでみるのはいかがでしょうか?

Ruby標準ライブラリはRuby自身で書かれており、/libディレクトリ以下にあります。

標準ライブラリには、OpenStructやBase64エンコーディング、Setデータ構造などが含まれています。

Set(集合)は配列に似ていますが、「すべての要素が一意である」という特別な性質を持っています。つまり、セットには重複が存在しないのです。

これはどのように実現されているのでしょうか? 背後には凝ったアルゴリズムがあるのでしょうか?

set.rbを見てみると、すぐにそれがHashオブジェクトによって実装されていることがわかります。

# Adds the given object to the set and returns self.  Use +merge+ to
# add many elements at once.
def add(o)
 @hash[o] = true
 self
end

alias << add

つまり、重複した要素を追加しても、すでに存在するかどうかをチェックする必要はありません。ハッシュキーが上書きされるだけで、重複は自然に排除されるのです。

Rubiniusを探検する

Rubyのソースコードを調べるもう一つの方法として、Rubiniusのような別の実装を見てみることもできます。

RubiniusのコードはMRIとは異なる方法で整理されているため、ここではGitHubの「find file」機能を使うのがおすすめです。

Ruby MRIのソースコードを読んで内部構造を学ぶ方法

Enumerableについて詳しく知りたい場合は、「enumerable」と入力するだけで、関連するすべてのファイルが一覧表示されます。

まとめ

ご覧いただいたように、あまり手間をかけずにRubyが裏側でどのように動作しているのかを学ぶことができます。ぜひ自分でも探検してみて、発見したことを周りの人にシェアしてください!

この記事が気に入ったら、ぜひニュースレターにご登録ください。下のフォームにメールアドレスを入力するだけで、無料のアップデートと限定コンテンツをお届けします。


  1. RubyとRuby on Railsでメソッド委譲を行う方法【delegate・Forwardable・SimpleDelegator徹底解説】

    この記事では、Rubyにおけるメソッド委譲(delegation)について詳しく解説します。 delegateメソッド、Forwardableモジュール、そしてSimpleDelegatorクラスの3つの手法を使いこなせるようになりましょう。 なぜ「委譲」が必要なのか? オブジェクト指向プログラミングにおいて、クラス同士が協調して動作する方法は主に2つあります。 継承(Inheritance) コンポジション(Composition) 継承では、親クラスが持つメソッド、定数、インスタンス変数の定義を、それを継承するすべての子クラスが共有するクラス階層を構築します。 例えば、Rubyではすべ

  2. あなたのコードはどこへ?RailsアプリでCRUD以外のロジックを置く場所の決め方

    Railsチュートリアルを終えて、いざ自分のアプリを作り始めると、混乱が始まります。「CRUD以外の一般的なロジックはどこに書けばいいの?」「Twitterからフォロワーを取得する処理はMVCのどこに収まるの?」——そんな疑問です。2人に聞けば4つの答えが返ってくる上、フォーラムのスレッドは気の利いた人たちが何時間も罵り合う場と化すこともあります。いずれにせよ、頭痛が残るだけです。 夢にまで見たアプリを作るには、何らかの汎用的なRails外のロジックがどうしても必要になります。では、コードをどこに置けば、シンプルさを保ったまま済むのでしょうか? まず手軽に試せる場所 既存のActiveReco