LaunchKit
All quizzes
Ruby 0 views

Splatting a frozen Hash with [*h]

Question

What is the return value of the following Ruby code?

h = { a: 21, b: 42 }.freeze

[*h] # => ???

The correct answer is

Explanation

TL;DR

The splat operator * expands an object inside an array literal by calling its to_a method. Hash#to_a returns a new array of [key, value] pairs, so [*h] returns [[:a, 21], [:b, 42]]. The hash itself is never mutated, so freeze is irrelevant and no FrozenError is raised.

Step by step

Hash#to_a builds a new array; the receiver is left untouched:

h = { a: 21, b: 42 }.freeze

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

[*h] splats that array's elements into the surrounding array literal:

h = { a: 21, b: 42 }.freeze

[*h] # => [[:a, 21], [:b, 42]]

Each [key, value] pair stays grouped: the splat flattens one level only, so the result is an array of pairs, not [:a, 21, :b, 42].

Under the hood

The splat does not know anything about hashes. It calls to_a on its operand and expands the resulting elements. You can watch it happen with a probe object:

class Probe
  def to_a
    puts "to_a called"
    [1, 2, 3]
  end
end

[*Probe.new] # => [1, 2, 3]

Output:

to_a called

An object without to_a is simply wrapped, and nil.to_a returns [], which is why splatting nil produces an empty expansion:

[*42]  # => [42]
[*nil] # => []

Edge cases

The double splat ** is the hash-flavored sibling: it expands key-value pairs into a hash literal (via to_hash) instead of an array:

h = { a: 21, b: 42 }.freeze

{ **h, c: 84 } # => {a: 21, b: 42, c: 84}

Both operators build new containers, so both work fine on frozen input. To go the other way, Array#to_h reassembles an array of pairs into a hash: [[:a, 21], [:b, 42]].to_h returns { a: 21, b: 42 }.

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.