Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double-splat operator destructively modifies hash – is this a Ruby bug?

I noticed what I find to be a very surprising behavior with the ** (double-splat) operator in Ruby 2.1.1.

When key-value pairs are used before a **hash, the hash remains unmodified; however, when key-value pairs are only used after the **hash, the hash is permanently modified.

h = { b: 2 }

{ a: 1, **h }        # => { a: 1, b: 2 }
h                    # => { b: 2 }

{ a: 1, **h, c: 3 }  # => { a: 1, b: 2, c: 3 }
h                    # => { b: 2 }

{ **h, c: 3 }        # => { b: 2, c: 3 }
h                    # => { b: 2, c: 3 }

For comparison, consider the behavior of the single-* operator on arrays:

a = [2]

[1, *a]     # => [1, 2]
a           # => [2]

[1, *a, 3]  # => [1, 2, 3]
a           # => [2]

[*a, 3]     # => [2, 3]
a           # => [2]

The array remains unchanged throughout.


Do we suppose the sometimes-destructive behavior of ** is intentional, or does it look more like a bug?

In either case, where is the documentation describing how the ** operator is meant to work?


I also asked this question in the Ruby Forum.

UPDATE

The bug is fixed in Ruby 2.1.3+.

like image 928
user513951 Avatar asked Apr 25 '14 00:04

user513951


People also ask

What is double splat operator in Ruby?

The double splat in Ruby is a super powerful feature which in Ruby 2.7 allows a hash to be passed as arguments to a method. It's very handy for when you'd like to transform some data, but want to have required arguments along with default values for other arguments.

What are two uses of the splat operator?

Single *Splat It can do things like combine arrays, turn hashes and strings into arrays, or pull items out of an array!

What does double asterisk mean in Ruby?

Turns out that two asterisks is Ruby's arithmetic operator for exponentiation. i.e. 2**3 means 2³ aka 2x2x2 aka 8.

What is splat operator?

What is the Splat Operator? The * (or splat) operator allows a method to take an arbitrary number of arguments and is perfect for situations when you would not know in advance how many arguments will be passed in to a method. Here's an example: def name_greeting(*names) names. each do |name| puts "Hello, #{name}!"


2 Answers

The answers to the question seem to be:

  1. It's probably a bug, rather than intentional.

  2. The behavior of the ** operator is documented very briefly in the core library rdoc.

Thanks to the suggestions of several commenters, I've posted the bug to the Ruby trunk issue tracker.


UPDATE:

The bug was fixed in changeset r45724. The comment there was "keyword splat should be non-destructive," which makes this an authoritative answer.

like image 173
user513951 Avatar answered Oct 12 '22 08:10

user513951


I noticed the diff between 2.1.5 and 2.3.1

Example is an irb method and a way of calling it

$ irb
>> def foo(opts) opts end
=> :foo
>> foo a: 'a', ** {a: 'b'}

In 2.1.5 the following results in retaining value

=> {:a=>"a"}

In 2.3.1 the value is 'b'

(irb):2: warning: duplicated key at line 2 ignored: :a
=> {:a=>"b"}

I am not sure which it should be?

In 2.3.1 the hash provided as double splat overrides the same key of the first item in a list.

like image 22
marekj Avatar answered Oct 12 '22 07:10

marekj