4

My understanding is that a single splat on a non-array object calls to_a and then dissociates the elements apart. And since nil.to_a is defined to be [], the following conversion happens:

[:foo, *nil, :bar]
# => [:foo, *nil.to_a, :bar]
# => [:foo, *[], :bar]
# => [:foo, :bar]

By analogy, I thought that a double splat on a non-hash object calls to_h and then dissociates the key-value pairs apart. And since nil.to_h is defined to be {}, I expected the following conversion to happen:

{"foo" => 1, **nil, "bar" => 2}
# => {"foo" => 1, **nil.to_h, "bar" => 2}
# => {"foo" => 1, **{}, "bar" => 2}
# => {"foo" => 1, "bar" => 2}

But actually, it raises an error: no implicit conversion of nil into Hash. Why does it behave like that?

Edit I am not asking about the reasoning behind the design. I am asking where my thinking is wrong regarding double splat.

sawa
  • 165,429
  • 45
  • 277
  • 381

2 Answers2

5

Well it's our human being super power to recognize patterns and predict things. However it's not always true. This is one example. Ruby is not consistent in splat and double splat. Your way of thinking is a good way to "remember" but it's not exactly the way Ruby works on splats.

See this bug report for more detail. In this bug report, Ruby's author Matz rather to remove the feature of being able to splat nil than add double splat to nil.

vutran
  • 2,145
  • 21
  • 34
1

The reason *nil works is because the splat operator works on anything that responds to to_a, and nil.to_a returns []. The reason **nil doesn't work is that nil doesn't respond to to_hash, which is to_a's double-splat counterpart.

If you wanted this behavior, you could monkey-patch NilClass:

class NilClass
  def to_hash
    {}
  end
end

{ "foo" => 1, **nil, "bar" => 2 }
# => { "foo" => 1, "bar" => 2 }
Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • 1
    So, you are saying that not `to_h` but `to_hash` applies in double splat? I thought that placing a single splat is explicit class conversion, and hence it is `to_a` rather than `to_ary` (my understanding is that `to_a` is explicit, `to_ary` is implicit), but it looks like that part of my thinking was wrong. – sawa Dec 07 '15 at 05:26
  • Yes, splat is implicit, not explicit conversion. – Jordan Running Dec 07 '15 at 05:28
  • That contradicts the accepted answer to [this question](http://stackoverflow.com/questions/9467395/whats-the-difference-between-to-a-and-to-ary). Is that answer wrong? – sawa Dec 07 '15 at 05:34
  • It also contradicts the very [weblog post](https://blog.pivotal.io/labs/labs/messages-not-types-exploring-rubys-conversion-protocols) that you linked. – sawa Dec 07 '15 at 05:43
  • Notice that the usage of `to_a` versus `to_ary` changed between Ruby 1.8 and Ruby 1.9, so you should not rely on old information. – sawa Dec 07 '15 at 05:46
  • 2
    Yes, it seems that I was mistaken. I've edited my answer to be accurate, although your attitude and your edits to my answer suggest that maybe you should have just researched and answered the question yourself. – Jordan Running Dec 07 '15 at 06:01
  • Warning: this can change the semantics of using `nil` as a method argument (just spent too long debugging this). Using `def x(a = "default", b:2) p [a, b] end`, `x(nil)` will throw a deprecation warning and try to treat the nil as an empty hash of keyword arguments, rather than setting `a` to `nil`. – Charlie Harding Jul 28 '20 at 23:01