LaunchKit
All quizzes
Ruby 0 views

The shared default array in Hash.new([])

Question

What's the return value of the following Ruby code?

h = Hash.new([])

h[:a] << 42

h[:b] # => ???

The correct answer is

Explanation

TL;DR

Hash.new([]) stores one single array as the default object. Every missing-key lookup returns that same array, and nothing is ever assigned into the hash. h[:a] << 42 therefore mutates the shared default in place, and h[:b], another missing key, returns that same mutated array: [42].

Step by step

h = Hash.new([])

h.default    # => []
h[:a]        # => []

h[:a] << 42  # => [42]

h            # => {}
h.default    # => [42]
h[:b]        # => [42]

h[:a] does not create the key :a. Hash#[] on a missing key just returns the default object, here the one array passed to Hash.new. << 42 then appends to that array in place. The hash itself stays empty, but its default object now contains 42, so every missing key appears to hold [42]:

h = Hash.new([])
h[:a] << 42

h[:b].equal?(h[:c]) # => true
h.keys              # => []

One object, many aliases. Any key you have never written to reads through to it.

The working idiom

To give each key its own array, use the block form and assign inside the block:

h = Hash.new { |hash, key| hash[key] = [] }

h[:a] << 42

h[:a] # => [42]
h[:b] # => []
h     # => {a: [42], b: []}

The block runs once per missing key, creates a fresh array, and stores it under that key before returning it. Hash.new(object) is only safe when the default is immutable (0, nil, a frozen string); with a mutable default like an array or hash, every key shares the same object.

Share this quiz

Comments

No comments yet. Be the first.

Only used to confirm and publish your comment. Never shown publicly, never shared.

Markdown: **bold**, `code`, ```fenced blocks```, > quotes, [links](url). HTML and images are not rendered.