web-dev-qa-db-ja.com

Ruby(2.2より前のバージョンの場合)で多くのシンボルを動的に作成するのはなぜ良い考えではないのですか?

ルビーのシンボルの機能は何ですか?文字列と記号の違いは何ですか?多くのシンボルを動的に作成するのはなぜ良い考えではないのですか?

62
Ikbear

記号は文字列に似ていますが、不変です。変更することはできません。

これらは一度だけメモリに入れられるため、ハッシュ内のキーなどに非常に効率的に使用できますが、プログラムが終了するまでメモリに残ります。これにより、誤用した場合にメモリを大量に消費します。

大量のシンボルを動的に作成する場合、プログラムが終了するまで解放できない大量のメモリを割り当てています。次のことがわかっている場合にのみ、(string.to_symを使用して)シンボルを動的に作成する必要があります。

  1. シンボルに繰り返しアクセスする必要があります
  2. それらを変更する必要はありません

前に述べたように、これらはハッシュのようなものに役立ちます-変数の値よりも変数のidentityを重視します。シンボルは、正しく使用されると、IDを渡すための読みやすく効率的な方法です。

シンボルの不変性について私が何を意味するのかを説明しますREあなたのコメント。

文字列は配列のようなものです。それらはその場で変更することができます:

12:17:44 ~$ irb
irb(main):001:0> string = "Hello World!"
=> "Hello World!"
irb(main):002:0> string[5] = 'z'
=> "z"
irb(main):003:0> string
=> "HellozWorld!"
irb(main):004:0> 

記号は数字に似ています。それらはその場で編集することはできません:

irb(main):011:0> symbol = :Hello_World
=> :Hello_World
irb(main):012:0> symbol[5] = 'z'
NoMethodError: undefined method `[]=' for :Hello_World:Symbol
    from (irb):12
    from :0
88
david4dev

シンボルは、使用される場所に関係なく、同じオブジェクトであり、同じメモリ割り当てです。

>> :hello.object_id
=> 331068
>> a = :hello
=> :hello
>> a.object_id
=> 331068
>> b = :hello
=> :hello
>> b.object_id
=> 331068
>> a = "hello"
=> "hello"
>> a.object_id
=> 2149256980
>> b = "hello"
=> "hello"
>> b.object_id
=> 2149235120
>> b = "hell" + "o"

同じ文字を含むという点で「同じ」である2つの文字列は、同じメモリを参照しない場合があります。これは、たとえばハッシュに文字列を使用している場合は非効率的です。

したがって、シンボルはメモリのオーバーヘッドを減らすのに役立ちます。ただし、シンボルは一度作成されるとガベージコレクションできないため、発生するのを待っているメモリリークです。何千ものシンボルを作成すると、メモリが割り当てられ、回復できなくなります。うわぁ!

9
stef

ある種のホワイトリストに対して入力を検証せずにユーザー入力からシンボルを作成することは特に悪い場合があります(たとえば、RoRのクエリ文字列パラメーターの場合)。ユーザー入力が検証なしでシンボルに変換されると、悪意のあるユーザーがプログラムに大量のメモリを消費させ、ガベージコレクションが行われない可能性があります。

悪い(ユーザー入力に関係なくシンボルが作成されます):

name = params[:name].to_sym

良い(シンボルは、ユーザー入力が許可されている場合にのみ作成されます):

whitelist = ['allowed_value', 'another_allowed_value']
raise ArgumentError unless whitelist.include?(params[:name])
name = params[:name].to_sym
6
Robert Kajic

Ruby 2.2.0以降を使用している場合は、 Ruby 2.2.0)に従ってガベージコレクションされるため、通常は動的に多数のシンボルを作成しても問題ありません。 -preview1announcement 、これには 新しいシンボルGCの詳細 へのリンクがあります。ただし、動的シンボルをIDに変換するある種のコードに渡すと(内部= Ruby Cソースコードで使用される実装コンセプト)、その場合、ピン留めされ、ガベージコレクションは行われません。これがどのくらい一般的に発生するかはわかりません。

記号は何かの名前と考えることができ、文字列は(大まかに)文字のシーケンスと考えることができます。多くの場合、記号または文字列のいずれかを使用することも、2つを組み合わせて使用​​することもできます。シンボルは不変です。つまり、作成後に変更することはできません。シンボルの実装方法では、2つのシンボルを比較してそれらが等しいかどうかを確認するのが非常に効率的であるため、それらをハッシュのキーとして使用する方が、文字列を使用するよりも少し高速です。シンボルには、start_with?など、文字列が行うメソッドがあまりないため、これらのメソッドを呼び出す前に、to_sを使用してシンボルを文字列に変換する必要があります。

シンボルの詳細については、ドキュメントを参照してください。

http://www.Ruby-doc.org/core-2.1.3/Symbol.html

2
David Grayson

開始Ruby 2.2 以上 シンボル は自動的にガベージコレクションされるため、これは問題にはなりません。

2
sidj