Symbol

Rubyを始めて間もなく、Symbolで躓いた覚えがあるので、忘れないうちに書いておきます。

Symbolとは、ちょー大雑把に言ってしまえば、余計なメソッドのない文字列です。こう言うとSymbolはStringと継承関係にあるように聞こえるかもしれませんが、継承関係はありません。to_s, to_symでお互いに変換できるので、関係は強いですが、別物です。

irb(main):008:0> :abc.to_s
=> "abc"
irb(main):009:0> 'abc'.to_sym
=> :abc

ここからは僕の勝手な推測ですので、ご注意を。

さて、SymbolもStringも文字列を表すのなら、なぜ別々に用意されているのか。それはStringには余計なメソッドが多すぎるからです。例えばHashのキーを考えます。Hashを

{ 'x' => 100, 'y' => 200 }

と書いてもいいのですが、このキーとなっているStringには沢山のメソッドが定義されています。キーとなっているStringの中身を変更する必要がない限り、これらのメソッドは必要ありません。キーとなる文字列に必要なのはキーとしての同一性を検証する機能だけであり、gsubやsplitなどは必要ありません。キーとなる文字列として必要のないメソッド群を省くと、いいことが3つあります。

1.可読性の向上
中身が変更されたり、加工されたりする文字列なのか、識別子としての文字列なのかが判別しやすくなります。

2.保守性の向上
余計なメソッドがないので、保持している文字列が変更されることもありませんので、HashのキーとなっているSymbolオブジェクトの中身が知らない間に変わってた、ということも起こりません。
僕はJavaからRubyに移って、Stringが不変クラスじゃないことに違和感を感じたのですが、Symbolが不変クラスであることが理解できたら、納得できました。

3.コストが下がる
余計なメソッドがないので、それらのための余計なインスタンス変数などを保持する必要がなくなり、メモリ使用量が減るはずです。

(注)id:secondlifeさんによると、StringとSymbolではインスタンスの生成のされ方が違うようです。

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


ちなみにSymbolのことを調べてみたら、:"文字列"という書き方もOKなんですね。だから以下のようなこともできます。おそらく使うことはないでしょうが。

irb(main):010:0> 'abc def'.to_sym
=> :"abc def"
irb(main):011:0> 'abc def\n'.to_sym
=> :"abc def\\n"