🔣

【Ruby】Symbol is 何

に公開

おはようございます、こんにちは、こんばんは。
スペースマーケットでWebエンジニアをしています、s0arです。

松屋のチゲ、うまい

社内で「結局Symbolってなんなの?」っていう会話をしました。
ふわっとした回答しかできませんでした。
次話すときには、計算通り、完璧〜に話せるようになろうと思いました。

※注: この記事は公式ドキュメントを読んで知識の整理・補足をするものです。真新しい内容はございませんのでご了承くださいませ。

Symbolさんの公式設定

https://docs.ruby-lang.org/ja/latest/class/Symbol.html

実装

シンボルを表すクラス。

:this_is_symbol.class
=> Symbol

はい

シンボルは任意の文字列と一対一に対応するオブジェクトです。
文字列の代わりに用いることもできますが、必ずしも文字列と同じ振る舞いをするわけではありません。同じ内容のシンボルはかならず同一のオブジェクトです。

symbol_taro = :taro
=> :taro
# 命名アホすぎて草
symbol_jiro_no_ani = :taro
=> :taro
symbol_taro.object_id
=> 19073628
symbol_jiro_no_ani.object_id
=> 19073628

immutableですよ、全部おんなじオブジェクトですよって話。

生成されたシンボルの一覧は Symbol.all_symbols で得られます。

はえ〜しらんかった

Rubyの内部実装では、メソッド名や変数名、定数名、クラス名などの名前を整数で管理しています。これは名前を直接文字列として処理するよりも速度面で有利だからです。そしてその整数をRubyのコード上で表現したものがシンボルです。

メソッド名や変数名、定数名、クラス名などの名前は内部的にはシンボルですってことやねたぶん

シンボルは、ソース上では文字列のように見え、内部では整数として扱われる、両者を仲立ちするような存在です。

Rubyの内部実装ようわからんけどポインタとアドレスみたいなもんか

用途

実用面では、シンボルは文字の意味を明確にします。`名前'を指し示す時など、文字列そのものが必要なわけではない時に用います。

  • ハッシュのキー { :key => "value" }
  • アクセサの引数で渡すインスタンス変数名 attr_reader :name
  • メソッド引数で渡すメソッド名 __send__ :to_s
  • C の enum 的な使用 (値そのものは無視してよい場合)

ハッシュのキーはよく使うよね。

メソッド引数で渡すメソッド名 __send__ :to_s

BaseObject#__send__は引数に渡すメソッド名をSymbolまたは文字列で指定できるんですよね。どっちがええの?
せっかくだから俺はこのBaseObject#__send__の実装を読むぜ!

BasicObject#__send__の定義部分
https://github.com/ruby/ruby/blob/4374236e959c1e585611acfc7a2e3d2142265ab0/vm_eval.c#L2578

__send__の内部実装呼び出し
https://github.com/ruby/ruby/blob/4374236e959c1e585611acfc7a2e3d2142265ab0/vm_eval.c#L1332
https://github.com/ruby/ruby/blob/4374236e959c1e585611acfc7a2e3d2142265ab0/vm_eval.c#L1300

文字列からSymbolへの変換処理呼び出し
https://github.com/ruby/ruby/blob/4374236e959c1e585611acfc7a2e3d2142265ab0/vm_eval.c#L1259

てなわけで、文字列からSymbolに変換する処理が入っているので、素直にSymbolで渡すのが良さそう。
send以外のメソッド名とか変数名をSymbol or 文字列で渡す系のメソッド(パッとと思いつかないけど)でも、内部的にはこれをやってそうなので基本的にSymbolを渡すのが良さそう。

GC

内部的にシンボルは

  • シンボルの情報を記録するテーブル
  • そのテーブルの要素を指し示すポインタ
    の2つにより実装されています。そのため同じシンボル(同じ文字列から作られたシンボル)を複製しても同じ要素へのポインタが使われるだけなのでメモリ使用量は普通の文字列と比べて少ないです。
# Symbol
symbol_taro = :taro
=> :taro
# 命名アホすぎて草
symbol_jiro_no_ani = :taro
=> :taro
symbol_taro.object_id
=> 19073628
symbol_jiro_no_ani.object_id
=> 19073628
# symbol_taroとsymbol_jiro_no_aniは同じオブジェクト

# 文字列
string_taro = "taro"
=> "taro"
# アホ命名
string_jiro_no_ani = "taro"
=> "taro"
string_taro.object_id
=> 2771582720
string_jiro_no_ani.object_id
=> 2243807940
# string_taroと別オブジェクトになってる

2.2.0 以降においては、テーブルに記録された情報は Ruby によって GC されます。すなわち、ある使わなくなったシンボルのテーブル上の情報はGCによって削除されます。
2.1 以前ではこの機能がなかったため、ユーザからの入力をシンボルに変換するようなプログラムは DoS に対して弱い可能性がありましたが、そのような問題は2.2以降では解決されました。

テーブルに積むだけ積んで消えないからメモリがパンパンになってたんやね

Symbol完全に理解した

今までふんわりした理解だったわけですが、これで完全に理解しました。
https://ja.wikipedia.org/wiki/ダニング=クルーガー効果

これで次に聞かれたときには完璧に答えられますね(たぶん)
というか聞かれたらこの記事読んでもらえばいいや

ふんわり理解はよくない、改めて実感した年の瀬でした。

GitHubで編集を提案
スペースマーケット Engineer Blog

Discussion