[#61822] Plan Developers Meeting Japan April 2014 — Zachary Scott <e@...>

I would like to request developers meeting around April 17 or 18 in this mo=

14 messages 2014/04/03
[#61825] Re: Plan Developers Meeting Japan April 2014 — Urabe Shyouhei <shyouhei@...> 2014/04/03

It's good if we have a meeting then.

[#61826] Re: Plan Developers Meeting Japan April 2014 — Zachary Scott <e@...> 2014/04/03

Regarding openssl issues, I=E2=80=99ve discussed possible meeting time with=

[#61833] Re: Plan Developers Meeting Japan April 2014 — Martin Bo煬et <martin.bosslet@...> 2014/04/03

Hi,

[ruby-core:62165] [ruby-trunk - Bug #9776] Ruby double-splat operator unexpectedly modifies hash

From: nobu@...
Date: 2014-04-26 01:54:34 UTC
List: ruby-core #62165
Issue #9776 has been updated by Nobuyoshi Nakada.

Backport changed from 2.0.0: UNKNOWN, 2.1: UNKNOWN to 2.0.0: DONTNEED, 2.1: REQUIRED

----------------------------------------
Bug #9776: Ruby double-splat operator unexpectedly modifies hash
https://bugs.ruby-lang.org/issues/9776#change-46322

* Author: Jesse Sielaff
* Status: Open
* Priority: Normal
* Assignee: Yukihiro Matsumoto
* Category: syntax
* Target version: current: 2.2.0
* ruby -v: ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-linux]
* Backport: 2.0.0: DONTNEED, 2.1: REQUIRED
----------------------------------------
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 splat (`*`) 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.

---------------

Tsuyoshi Sawada has also highlighted that the expression's result is the self-same object as the original hash:

~~~
h.object_id == { **h, c: 3 }.object_id # => true
~~~

---------------

I investigated `parse.y` to try to determine the error there, but I couldn't narrow it down any further than the `list_concat` or `rb_ary_push` function calls in the `assocs  :` block of the grammar.

Without exhaustively examining the C source, I think the best clue to the mechanism behind the erroneous behavior might be the following:

~~~
h = { a: 1 }
{ **h, a: 99, **h } # => {:a=>99}
~~~

That we don't see `{:a=>1}` illustrates that `h[:a]` is already overwritten by the time the second `**h` is evaluated.

---------------

Here is the use case that led me to this discovery:

~~~
def foo (arg) arg end

h = { a: 1 }

foo(**h, b: 2)

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

In the above example, I don't want `{ b: 2 }` permanently added to my existing hash. I'm currently solving it like this:

~~~
h = { a: 1 }

foo(**h.dup, b: 2)

h # => { a: 1 }
~~~

The call to #dup feels unnecessary, and is inconsistent with the analogous behavior when using the single `*` operator. If this bug is fixed, I'll be able to eliminate that call.




-- 
https://bugs.ruby-lang.org/

In This Thread

Prev Next