diff --git a/README.adoc b/README.adoc index 132e6c29..a0a6904b 100644 --- a/README.adoc +++ b/README.adoc @@ -4294,6 +4294,45 @@ hash = { one: 1, two: 2, three: 3 } Avoid the use of mutable objects as hash keys. +=== No Mutable Defaults [[no-mutable-defaults]] + +Avoid the use of shared mutable objects as hash default values. + +Creating a Hash in such a way will share the default value +across all keys, causing unexpected behavior when modifying it. + +For example, when the Hash was created with an Array as the argument, +calling `hash[:foo] << 'bar'` will also change the value of all +other keys that have not been explicitly assigned to. + +[source,ruby] +---- +# bad +Hash.new([]) +Hash.new({}) +Hash.new(Array.new) +Hash.new(Hash.new) + +# okay -- beware this will silently discard mutations and only remember assignments +Hash.new { Array.new } +Hash.new { Hash.new } +Hash.new { {} } +Hash.new { [] } + +# good - frozen solution will raise an error when mutation is attempted +Hash.new([].freeze) +Hash.new({}.freeze) + +# good - using a proc will create a new object for each key +h = Hash.new +h.default_proc = ->(h, k) { [] } +h.default_proc = ->(h, k) { {} } + +# good - using a block will create a new object for each key +Hash.new { |h, k| h[k] = [] } +Hash.new { |h, k| h[k] = {} } +---- + === Hash Literals [[hash-literals]] Use the Ruby 1.9 hash literal syntax when your hash keys are symbols.