[#62904] [ruby-trunk - Feature #9894] [Open] [RFC] README.EXT: document rb_gc_register_mark_object — normalperson@...
Issue #9894 has been reported by Eric Wong.
3 messages
2014/06/02
[#63321] [ANN] ElixirConf 2014 - Don't Miss Jos辿 Valim and Dave Thomas — Jim Freeze <jimfreeze@...>
Just a few more weeks until ElixirConf 2014!
6 messages
2014/06/24
[ruby-core:63198] [ruby-trunk - Bug #9776] Ruby double-splat operator unexpectedly modifies hash
From:
nagachika00@...
Date:
2014-06-16 16:00:45 UTC
List:
ruby-core #63198
Issue #9776 has been updated by Tomoyuki Chikanaga.
Backport changed from 2.0.0: DONTNEED, 2.1: REQUIRED to 2.0.0: DONTNEED, 2.1: DONE
Backported into `ruby_2_1` branch at r46451.
----------------------------------------
Bug #9776: Ruby double-splat operator unexpectedly modifies hash
https://bugs.ruby-lang.org/issues/9776#change-47249
* Author: Jesse Sielaff
* Status: Closed
* 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: DONE
----------------------------------------
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/