Bug #16010
closedSole single-splatted variable with an array object in an assignment construction gobbles an additional nesting of array
Description
When a single splatted variable appears as the sole argument on the left side of assignment with an array on the right side, that variable is assigned the object as is on the right side:
*foo = ["a"]; foo # => ["a"]
This behavior looks inconsistent to me, and I am suspecting it is a bug from the reasons below.
First, all other constructions that involve assignment of objects to variables work in the same way to one another, but in a different way from the above. That is, they add another nesting level of an array:
instance_exec(["a"]){|*foo| foo} # => [["a"]]
->*foo{foo}.call(["a"]) # => [["a"]]
def baz(*foo); foo end; baz(["a"]) # => [["a"]]
Second, if the object on the right side of the assignment construction in question is not an array, then another level of nesting is added:
*foo = "a"; foo # => ["a"]
The splat on a variable can be understood to collect the objects into an array. However, in the first example above in which the right side object is an array, all of a sudden, the additional nesting level of array becomes absent. It is not obvious why it behaves differently when the object to be collected is already an array.
Third, when there is no remaining object for the splatted variable, the variable is assigned an empty array,
*foo, bar = "baz"; foo # => []
and when more than one objects remain for the splatted variable, the variable is assigned an array that includes those objects, even if they are arrays:
*foo, bar = ["a"], ["b"], "c"; foo # => [["a"], ["b"]]
But when there is exactly one object that corresponds to the splatted variable, that object is not included in an array, but is given as is.
In short, I believe the correct behavior should be as follows:
*foo = ["a"]; foo # => [["a"]]
Updated by jeremyevans0 (Jeremy Evans) over 4 years ago
- Status changed from Open to Rejected
I believe this behavior is expected/spec. Ruby's behavior when given an multiple-assignment expression such as this:
*foo = v
Is to treat it similar to:
*foo = Array(v)
So the reason that *foo = "a"
and *foo = ["a"]
result in the same foo
is simply due to the fact that Array("a")
is the same as ["a"]
. If you change how string is converted to array, you get different results:
class String; def to_ary; [self, upcase]; end end
*foo = "a"; foo # => ["a", "A"]
The reason that is appears that there is an extra array wrapping in this code:
*foo, bar = ["a"], ["b"], "c"; foo # => [["a"], ["b"]]
Is because the RHS is an array, even if it doesn't have explicit brackets. It is identical to:
*foo, bar = [["a"], ["b"], "c"]; foo # => [["a"], ["b"]]