Rails using Symbol vs String as key in params hash
If we use a string as a Hash key, Ruby needs to evaluate the string and look at it's contents (and compute a hash function on that) and compare the result against the (hashed) values of the keys which are already stored in the Hash.
If we use a symbol as a Hash key, it's implicit that it's immutable, so Ruby can basically just do a comparison of the (hash function of the) object-id against the (hashed) object-ids of keys which are already stored in the Hash. (much faster).
But the thing is in Rails params
which is instance of HashWithIndifferentAccess
, if we write params[:some_key]
it converts :some_key
to 'some_key'
and then it tries to look for the key in params hash. line 159
def convert_key(key)
key.kind_of?(Symbol) ? key.to_s : key
end
so if the lookup is slow with String as key in Hash, why does HashWithIndifferentAccess
converts symbol key to string.
The reason used to be security. It's no longer relevant in Ruby 2.2 or later.
Before Ruby 2.2 symbols were not garbage collected. This means that once a symbol was created via a literal ( :my_symbol
) or #to_sym
it would be around forever.
If Rails used symbols instead of strings then it would create symbols corresponding to names of params in a request. An attacker could send requests with params named param1
, param2
, ... and exhaust server memory by making the app allocate hundreds of thousands of symbols.
It's no longer the case in Ruby 2.2 or later
:symbol.to_s
will always create a new instance string, whereas, "string".to_sym
will always produce same symbol.
p "string".to_sym.object_id
#=> 272028
p "string".to_sym.object_id
#=> 272028
p :symbol.to_sym.to_s.object_id
#=>70127441809260
p :symbol.to_sym.to_s.object_id
#=>70127441809160
Hence, it seems that designers of the class store the keys as strings to avoid unnecessary creation of strings during access, especially if one is using hash.keys
method (it returns keys as strings).
上一篇: 如何证明C#中字符串的不可变性?