RubyのSymbol

Ruby最初学んだとき、SymbolとStringの違いが解って無くて、なら全部StringでいいやとSymbol全く使わなかった時期がありました。今は意図的に使い分けるようにしています。Lost-Season: Rubyのシンボル で疑問点が上がっていたので、初心者向けにSymbolについて説明してみます。

まず使い道ですが、文字の定義明確にしたいときに使うことが多いです。たとえばhashのkeyだったり、アクセサの引数で渡すインスタンス変数名だったり、alias_methodの引数で渡すメソッド名だったりと、文字に意味づけしたい時に使えます。このようなときにSymbol使うことによって、ソースがすっきりして可視性が上がります。

また、symbol使うと速度が向上します。これは、'a' と書くと毎回Stringの'a'生成しコストが発生しますが、:aと書くと初回にしかコストが発生しません*1。比較も高速に行えます。

ただ、Hashのkeyとして使うとき、人によっては

 hash = {:a => 'foo', :b => 'bar'}

と書いたり、

 hash = {'a' => 'foo', 'b' => 'bar'}

と書いたりし、keyがsymbolかstringか解らなくて結局の所ソース実際に確かめるケースが発生します。Railsではそれ取り除くために、active_supportで定義されているHashWithIndifferentAccessクラス使い回避しています。これはHashのkeyがsymbolだった場合はstringにキャストされ、symbolでもstringでもvalueにアクセスできるようになっています。これで

 url_for :controller => 'foo', :action => 'bar'
 url_for 'controller' => 'foo', 'action' => 'bar'

とHashのkeyがstring, symbolどちらでも利用することができます。しかしsymbolからstringへのキャスト毎回するため、速度的には遅くなってしまいます*2が、使いやすさ優先の考え方なのでしょう。

*1: 'a'.equal? 'a' はfalseだが:a.equal? :aはtrue

*2:ベンチマーク取ってみたところ、2倍強差(でも10万回ループで0.275367秒 => 0.664770秒 だけど)