Rails 使用符号与字符串作为参数哈希中的键

问题描述 投票:0回答:3

如果我们使用字符串作为哈希键,Ruby 需要评估该字符串并查看其内容(并计算其哈希函数),并将结果与已存储在哈希。

如果我们使用符号作为哈希键,则隐含着它是不可变的,因此 Ruby 基本上可以将对象 id 的(哈希函数)与已存储的键的(哈希)对象 id 进行比较在哈希中。 (快得多)。

但问题是在 Rails

params
中,它是
HashWithIndifferentAccess
的实例,如果我们编写
params[:some_key]
,它会将
:some_key
转换为
'some_key'
,然后尝试在 params 哈希中查找键。 159 号线

 def convert_key(key)
    key.kind_of?(Symbol) ? key.to_s : key
  end

所以如果在 Hash 中使用 String 作为键查找很慢,为什么

HashWithIndifferentAccess
将符号键转换为字符串。

ruby-on-rails ruby hash
3个回答
7
投票

曾经的原因是安全。它在 Ruby 2.2 或更高版本中不再相关。

在 Ruby 2.2 之前,符号不会被垃圾回收。这意味着一旦通过文字 (

:my_symbol
) 或
#to_sym
创建了一个符号,它就会永远

如果 Rails 使用符号而不是字符串,那么它将创建与请求中的参数名称相对应的符号。攻击者可以发送带有名为

param1
param2
、... 的参数的请求,并通过让应用程序分配数十万个符号来耗尽服务器内存。

在 Ruby 2.2 或更高版本中不再是这种情况


0
投票

:symbol.to_s
将始终创建一个新的实例字符串,而
"string".to_sym
将始终生成相同的符号。

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

因此,该类的设计者似乎将键存储为字符串,以避免在访问期间不必要地创建字符串,特别是如果使用

hash.keys
方法(它将键作为字符串返回)。


0
投票

对于整数键,HashWithIndeffrentAccess 返回 nil,因此使用 String 是安全的

pry(main)> hash = HashWithIndifferentAccess.new
#=> {}
[2] pry(main)> hash[1] = "a"
#=> "a"
[3] pry(main)> hash
#=> {1=>"a"}
[4] pry(main)> hash["1"]
#=> nil

我们也不能使用这样的整数

 pry(main)> hash[:1] = "a"
 SyntaxError: unexpected integer literal, expecting literal content or terminator or tSTRING_DBEG or tSTRING_DVAR
© www.soinside.com 2019 - 2024. All rights reserved.