在 Ruby 中冻结 HashWithIndifferentAccess

Freezing HashWithIndifferentAccess in Ruby

我试图冻结 Ruby 中的嵌套 HashWithIndifferentAccess 并遇到了一些意外行为。没有用 .with_indifferent_access 包装内部或外部散列,它按预期工作:

v = { 'a' => { 'b' => 2 }.freeze }.freeze
v['a'].frozen? => true

v_1[:a]v_2[:a]都是HashWithIndifferentAccess的,但是只有v_1[:a]是冻结的。这是为什么?

v_1 = { 'a' => { 'b' => 2 }.with_indifferent_access.freeze }.with_indifferent_access.freeze
v_1[:a].frozen? => true
v_2 = { 'a' => { 'b' => 2 }.freeze }.with_indifferent_access.freeze
v_2[:a].frozen? => false

提前致谢!

如果您查看 with_indifferent_accessimplementation details,您会发现它实际上是原始哈希的 dupdup-ed 哈希未被冻结。通过在返回的散列(来自 with_indifferent_access)上调用 freeze,你冻结它。

所以我们正在研究嵌套散列在

之间的行为有何不同
v_1 = { a: { b: 2 } }.with_indifferent_access
v_2 = { a: { b: 2 }.with_indifferent_access }.with_indifferent_access

当您调用 Hash#with_indifferent_access, it creates a new ActiveSupport::HashWithIndifferentAccess object; and then calls #update to insert all of the key/value pairs from the original hash into the new object (ActiveSupport::HashWithIndifferentAccess#update) 时,它使用嵌套哈希调用 #convert_values

def convert_value(value, options = {})
  if value.is_a? Hash
    if options[:for] == :to_hash
      value.to_hash
    else
      value.nested_under_indifferent_access
    end
    ...

所以 { b: 2 }{ b: 2 }.with_indifferent_access 都会有 #nested_under_indifferent_access 调用它们。但对于 Hash 来说,这是一种不同于 HashWithIndifferentAccess 的方法。在 core_ext 文件中,Hash#nested_under_indifferent_access 调用 HashWithIndifferentAccess.new(self),但 HashWithIndifferentAccess#nested_under_indifferent_access 只是 returns self.

所以{'b' => 2}.nested_under_indifferent_access returns一个新的对象,但是{'b' => 2}.with_indifferent_access.nested_under_indifferent_access不对对象做任何事情。这就是为什么如果第一个被冻结,你会得到一个不同的(默认情况下未冻结)对象,如果第二个被冻结,它会保持冻结状态。