Rubyのシンボルについて改めて学習して説明できるようにしてみた

はじめに

僕が初めて学習したプログラミング言語がRubyだったため「プログラミングというものにはシンボルというものがあり文字列っぽく扱えるものがある」と曖昧な解釈をしていました。

ただ、他の言語を学習してみるとRubyのシンボルのようなものなく、そこで改めてシンボルとは何なのか?についてまとめました。


シンボルとは

まずはRubyの公式ドキュメントを確認したところ次のように説明されています。

シンボルを表すクラス。シンボルは任意の文字列と一対一に対応するオブジェクトです。

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

シンボルオブジェクトは以下のようなリテラルで得られます。

:symbol
以下省略

引用:Ruby リファレンスマニュアル

個人的に公式ドキュメントを見ただけでは「分かりそうだけど分からん」という感じだったため、まずは公式ドキュメントの内容を整理した内容を解説していきたいと思います。

  • シンボルとはクラスである
  • 任意の文字列と一対一のオブジェクトである
  • 文字列の代わりとして使える
  • でも、文字列と同じ振る舞いをするわけではない
  • シンボルは同じ内容であれば必ず同一のオブジェクトである
  • :symbol これがシンボル

シンボルとはクラスのこと

様々なRubyの参考書で説明されているので、実際に確認だけしてみます。

'ruby'.class
#=> String

1234.class
#=> Integer

:ruby.class
#=> Symbol

シンボルはSymbolクラスに属しているオブジェクトである。


任意の文字列と一対一のオブジェクトである

シンボルは文字列と一対一で対応しているオブジェクトだと説明されていますが、説明を読んだだけでは「理解できた」とは言えないので、まずは確認してみます。

確認方法は次になります。

  • 文字列がシンボルに対応していることをinternメソッドを使って確認
  • 逆にシンボルが文字列に対応していることをto_sメソッドを使って確認

Ruby リファレンス - String#intern Ruby リファレンス - String#to_s

'ruby'.intern
#=> :ruby

:ruby.to_s
#=> "ruby"

文字列'ruby'とシンボル:rubyが一対一で対応していることが分かりました。


シンボルは文字列の代わりとして使うことができる

Ruby学習をスタートさせた当時は、あまり疑問に思いもしなかった「シンボルは文字列の代わりとして使える」という説明に関して「文字列とは具体的に何が違うのか」「シンボルを使うメリットとは」についてまとめました。


  • 文字列との違いとは?
  • 文字列の代わりとしてシンボルを使うと何か嬉しいの?

ここでは 「シンボルを使うメリット」 についてまとめたので解説していきます。

文字列との違いに関しては後ほど説明する 「文字列と同じ振る舞いをするわけではない」 にて説明するので一旦省略します。


シンボルのメリット

  1. 「:」コロンを前置するだけなのでタイピングが楽になる
  2. シンボルは文字列に比べ処理速度が早い(シンボルは内部的に整数として扱われている)

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

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

引用: Ruby リファレンスマニュアル


  1. メモリ消費が文字列と比べ少ない
  2. ハッシュが見やすくなる
# 文字列の場合
people = {"name" => "ryutaro", "age" => 12}

# シンボルの場合
people = {name: "ryutaro", age: 12}

以上のようにシンボルには様々なメリットがあります。


文字列とシンボルの振る舞いの違い

必ずしも文字列と同じ振る舞いをするわけではありません。同じ内容のシンボルはかならず同一のオブジェクトです。


公式ドキュメントによると文字列とシンボルには振る舞いの違いがあるようです。

シンボルのメリットでも説明した通り速度面や可読性の違いもありますが、それだけではなく文字列とは違い、シンボルは必ず同一のオブジェクトになります。


ただ、これだけでは分かりづらいと思うので、まずは確認してみましょう。
確認方法はobject_idメソッドを使います。このメソッドは、そのオブジェクトに対して割り当てられているidを整数で確認することができます。
この整数はオブジェクト毎に一意であるため、シンボルが同一のオブジェクトなのかを調べることができます。


まずは文字列で調べてみます。

'ruby'.object_id
#=> 70308334456660

'ruby'.object_id
#=> 70308334442480

全く同じ文字列のはずなのに1回目と2回目ではオブジェクトidが違います。これは別々のオブジェクトがそれぞれ作成されていることを意味しています。


続いてはシンボルを調べてみます。

:ruby.object_id
#=> 676828

:ruby.object_id
#=> 676828

シンボルの場合は、全く同じオブジェクトidが出力されました。これは全く同じオブジェクトを指していることを意味しています。


以上で、「文字列とシンボルとの振る舞いの違い」についてと「同じ内容のシンボルはかならず同一のオブジェクトである」ということが確認できました。


シンボルの使い道

シンボルとは何なのかについて理解したので「シンボルはどんなときに使うのか」についてです。
シンボルの代表的な使い道としてハッシュのキーとして使われることがあります。これにより文字列よりも高速で値を取り出すことができるようになります。


# 文字列の場合
people = {"name" => "ryutaro", "age" => 12}
people["name"]
#=> "ryutaro"

# シンボルの場合
people = {name: "ryutaro", age: 12}
people[:name]
#=> "ryutaro"

※説明していませんでしたが、シンボルをキーとして使う場合はより簡潔に記述できるようにRubyでは「:」コロンを前置ではなく後置して記述することができます。


シンボルとは【まとめ】

これまでのことをまとめます。

  • 文字列のように扱うことができるSymbolクラスのオブジェクト
  • 内部的には整数として扱われるため高速に処理される
  • シンボルは同一オブジェクトであるためメモリ効率が良い

これからシンボルとは何かについて質問された時は一言で次のように答えることができそうです。
「シンボルとは、文字列のように扱えるけど内部的には整数として扱われているから高速に処理ができるかつメモリ効率も良いので代表的にはハッシュのキーとして使うことが多いものです。」


Written by@Ryutaro
日々学習した技術系のアウトプットをしていきます。学習内容: Ruby, Ruby on Rails, Go, TypeScript, Docker

GitHub