Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyのシンボルとは?仕組みと使い方をわかりやすく解説

Rubyのシンボルは、次のように書きます。

:testing

シンボルを変数の一種だと勘違いする人がいますが、シンボルは変数とはまったく関係ありません。

むしろシンボルは文字列に近い存在です。

では、Rubyにおけるシンボルと文字列の違いは何なのでしょうか?

一言でいえば、文字列は「データ」を扱うためのもので、シンボルは「識別子(名前)」として使われるものです。

これが両者の最大の違いです。シンボルは単なる別種の文字列ではなく、まったく異なる目的を持っています。

シンボルを使うべき場面

シンボルの最も一般的な用途は、メソッド名やインスタンス変数名を表すことです。

例:

attr_reader :title

このattr_readerに続く:titleは、インスタンス変数@titleを表すシンボルです。

また、ハッシュのキーとしてもシンボルがよく使われます。

例:

hash = {a: 1, b: 2, c: 3}

シンボルをキーに使うメリットは何でしょうか?

まず見た目がすっきりすること、そしてシンボルはイミュータブル(不変)であることです。さらにベンチマークを取ると、文字列キーはシンボルキーと比べて約1.7倍遅いという結果が出ます。

ここでいうイミュータブルとは、すべてのシンボルが一意であり、内容を変更できないという意味です。

:rubyguides.object_id
# 437768

:rubyguides.object_id
# 437768

この2つのシンボルは同じobject_idを持っているため、同一のオブジェクトであることがわかります。

さらに、シンボルはsendのようなメタプログラミング系メソッドでも活用できます。

[1,2,3].send(:first)

まとめると:

メソッドなどの名前やラベルにはシンボルを使い、個々の文字といったデータそのものを扱いたい場合には文字列を使うのが基本です。

文字列とシンボルの相互変換

必要であれば、シンボルを文字列に変換できます。

なぜそんなことをするのか? それは、シンボルが持つメソッドはStringクラスの一部に限られているからです。

シンボルは変更できないため、個々の文字を操作したい場合は文字列が必要になります。

変換にはto_sメソッドを使います。

たとえばmethod_missingを使うとき、呼び出された存在しないメソッドの名前はシンボルとして渡されます。このメソッド名が特定のパターン(たとえば?で終わるかどうか)に一致するか確認したいことがあるでしょう。

例:

def method_missing(method_name, *args, &block)
  if method_name.to_s[-1] == "?"
    # 何らかの処理
  else
    super
  end
end

逆に、文字列オブジェクトをシンボルオブジェクトに変換することもできます。

そのためのメソッドがString#to_symです。

"rubyguides".to_sym

:rubyguides

シンボルの配列を作る方法

シンボルの配列を作りたいときは、次のようなコードが使えます。

symbols = %i(a b c)

[:a, :b, :c]

これならコロンやカンマをいちいち入力する手間が省けます。

文字列版の%wとよく似ていますね。

strings = %w(a b c)

["a", "b", "c"]

Symbol GC(上級者向け)

シンボルにはもうひとつ興味深い事実があります。実は複数の種類が存在するのです。

その理由は、Ruby 2.2より前のバージョンではシンボルがガベージコレクション(GC)の対象外だったからです。つまり、不要になったシンボルは、文字列やハッシュ、配列といった通常のRubyオブジェクトのようにメモリから解放されませんでした。

実際に例を見てみましょう:

p Symbol.all_symbols.size
# 2443

('aa'..'aj').map(&:to_sym)

GC.start
p Symbol.all_symbols.size
# 2453

新しいシンボルを10個作成したので、合計数も10増えています。期待どおりの動作ですね。

しかしRuby 2.2以降では、これらのシンボルは一時的なものであり、コード内の他の場所で使われていないため、メモリから削除されます。

Symbol GCが有効になっているバージョンのRubyでこのコードを実行すると、両方のカウントは同じ値になるはずです。

一方で、決してメモリから解放されないシンボルもあり、これらは「不滅のシンボル(immortal symbols)」と呼ばれます。

ObjectSpaceモジュールを使えば、その数を確認できます。

require 'objspace'

ObjectSpace.count_symbols

{
  :mortal_dynamic_symbol=>3,
  :immortal_dynamic_symbol=>5,
  :immortal_static_symbol=>3663,
  :immortal_symbol=>3668
}

注目すべきは、:a1のように直接記述して作られたシンボルは自動的に不滅のシンボルになるという点です。また、新しいメソッドを定義すると、それに対応するimmortal_static_symbolも一緒に生成されます。

では、「可滅のシンボル(mortal symbols)」はどこから来るのでしょうか?

答えは、to_symメソッドによって文字列から変換されたシンボルです。

ObjectSpace.count_symbolsを使えば、自分でも確認できます。

ちなみにimmortal_dynamic_symbolとは、可滅のシンボルが不滅のシンボルへと昇格したものです。これは、可滅のシンボルと同じ名前でメソッドを作成した場合などに発生します。

まとめ

この記事で学んだことは以下のとおりです。

  • シンボルはイミュータブル(不変)である
  • シンボルは値へのポインタではなく、それ自体が値である
  • 文字列はデータ向け、シンボルは識別(アイデンティティ)向け
  • 文字列とシンボルは相互に変換できる
  • Ruby 2.2でSymbol GCが導入され、一時的なシンボルが解放されるようになった

新しい発見があったなら嬉しいです!

ぜひこの記事をシェアして、より多くの人に届けてください 🙂

  1. Ruby 2.6のMJITとは?仕組みとパフォーマンスを徹底解説

    Rubyのパフォーマンスは、バージョンが上がるごとに大きく向上してきました。そしてRuby開発チームは、さらに速いRubyを実現するために全力を尽くしています。 その取り組みのひとつが「3×3プロジェクト」です。 その目標とは? Ruby 3.0を、Ruby 2.0の3倍速くすることです。 このプロジェクトの一環として誕生したのが、新しいMJITコンパイラです。これが本記事のテーマとなります。 MJITとは何か MJITは「Method Based Just-in-Time Compiler(メソッドベースのジャストインタイムコンパイラ)」の略称です。 これは具体的にどういう意味でしょうか?

  2. Windows Sysinternalsとは?概要から使い方まで徹底解説

    自分のWindowsパソコンを完全にコントロールできたら素晴らしいと思ったことはありませんか?ほぼすべてのWindowsプロセスやアプリケーションの内部を覗き見できるだけでなく、アプリがアクセスしているファイルやレジストリエントリをリアルタイムで確認できるのは、実に魅力的な機能です。 Sysinternalsは、最も信頼性が高く強力なWindowsシステム管理ツールキットのひとつです。そのほとんどのユーティリティはシンプルなEXEファイルなので、USBフラッシュドライブに入れておけば、持ち運び可能なシステム管理ツールとしていつでも活用できます。 Windows Sysinternalsとは?