ハッシュでよく使われるシンボルですが、ハッシュ以外でシンボルとシングルクオートが並んでいるコードを見た時、なぜ違う書き方をしているのか理解できないことがありました。
私にはどちらも同じものに見えており「そこ、シングルクオートで書いてしまえばいいのでは?」と思ったからです。
しかし、実際は文字列に見えるようでシンボルと文字列は異なるものであるということを知りました。
そこで、シンボルについての理解を深めるために、改めてまとめてみようと思いました。
シンボルとは
# シンボルは:strを指す
str = :str
Rubyのリファレンスマニュアルには以下のように説明されています。
シンボルを表すクラス。シンボルは任意の文字列と一対一に対応するオブジェクトです。
Rubyの内部実装では、メソッド名や変数名、定数名、クラス名など の`名前'を整数で管理しています。これは名前を直接文字列として処理するよりも 速度面で有利だからです。そしてその整数をRubyのコード上で表現したものがシンボルです。(中略)シンボルは、ソース上では文字列のように見え、内部では整数として扱われる、両者を仲立ちするような存在です。
私が最初に理解につまずいてしまったのは、どうやら「シンボルの見た目が文字列のように見えてしまっていたこと」が原因だったようです。
そもそも、整数として扱われるシンボルと文字として扱われる文字列では、異なるオブジェクトであることがわかりました。
以上のことを踏まえ、文字列との比較もしながら、シンボルについてより詳しく書いていこうと思います。
1.Symbolクラスのオブジェクトである
シンボルはSymbolクラスのオブジェクト、文字列はStringクラスのオブジェクトです。
2.immutable(変更不可)である
Rubyでは、シンボルはimmutableなので破壊的な変更をすることができません。一方で、文字列はmutableなので破壊的な変更を加えることができます。
str1 = 'str'
str2 = :str
# .upcase!で全ての小文字を対応する大文字に置き換えた文字列を返す
str1.upcase! # => 'STR'
str2.upcase! # => NoMethodErrorが発生する
3.同値ならば必ず同一である
Rubyにおける同値性は、オブジェクトの一致/不一致に関わらず値が等しいことを、同一性は、オブジェクトが等しいことを意味しています。シンボルは、同値の場合同一であることが約束されます。
# 同じ名前の文字列とシンボルから生成されるインスタンスを比較する
str1 = "str"
str2 = "str"
str3 = :str
str4 = :str
# 論理演算子==でお互いの値が一致するかを確かめる
puts str1 == str2 # =>true
puts str3 == str4 # =>true
# .equal?でお互いのObject#object_idが一致するかを確かめる
# 二つのオブジェクトが同じかどうかがわかる
puts str1.equal?(str2) # =>false
puts str3.equal?(str4) # =>true
4.文字列よりも比較の処理が速い
シンボルは一度生成されると同じ領域を使用します。そのため、使用するたびに異なる領域を介す文字列よりも、メモリの効率がよく、処理の速度が速くなります。
補足
文字列自体をデータとして扱いたい場合は素直に文字列を使用した方がいいです。
結論
・Symbolクラスのオブジェクトである
・immutable(変更不可)である
・同値ならば必ず同一である
・文字列よりも比較の処理が速い
参考記事
「Ruby 3.2 リファレンスマニュアル > Symbolクラス」
「Ruby 3.2 リファレンスマニュアル > Stringクラス」
「Rubyの文字列とシンボルの違いをキッチリ説明できる人になりたい」
「Rubyのシンボルを詳しくまとめてみた。」
「Rubyのシンボルを丁寧に理解する」
「【Ruby入門】コロン記号の意味とシンボルのやさしいまとめ!」