Reputation: 8787
Although the splat (*
) construct is commonly referred to as the splat operator, it is clear that it is a different beast, compared to other unary operators like the negation (!
) operator.
The splat works fine on it's own (i.e. not wrapped in brackets) when used in assignment (=
), but produces an error when used with conditional assignment (||=
). Example:
a = *(1..3)
#=> [1, 2, 3]
b ||= *(1..3)
SyntaxError: (irb):65: syntax error, unexpected *
I am not looking for alternative ways of doing the same thing, but looking for someone with a better understanding of the Ruby internals to explain why this usage of the splat construct works in the first case but not in the second.
Upvotes: 6
Views: 538
Reputation: 1598
By splatting *(1..3)
in your expression you get 1, 2, 3
, and when it is assigned it behaves like mass assignment, but ruby seems not to support it for conditional assignment.
I.e. a=1,2,3
is just a syntax sugar of ruby. Just use an array explicitly:
a ||= [*1..3] #=> [1, 2, 3]
And actually you only use part of splat functionality here - it's autoconversion to array :) so you can simply do:
a ||= (1..3).to_a #=> [1, 2, 3]
Upvotes: 0
Reputation: 35493
Here's my understanding of the practical goal of splat. This is for Ruby 2.2 MRI/KRI/YARV.
Ruby splat destructures an object into an array during assignment.
These examples all provide the same result, when a
is falsey:
a = *(1..3)
a = * (1..3)
a =* (1..3)
a = *1..3
a = * 1..3
a = * a || (1..3)
a = * [1, 2, 3]
=> [1, 2, 3]
The splat does the destructuring during the assigment, as if you wrote this:
a = [1, 2, 3]
(Note: the splat calls #to_a
. This means that when you splat an array, there's no change. This also means that you can define your own kinds of destructuring for any class of your own, if you wish.)
But these statements fail:
*(1..3)
* 1..3
* [1,2,3]
false || *(1..3)
x = x ? x : *(1..3)
=> SyntaxError
These statements fail because there's no assignment happening exactly when the splat occurs.
Your question is this special case:
b ||= *(1..3)
Ruby expands this to:
b = b || *(1..3)
This statement fails because there's no assignment happening exactly when the splat occurs.
If you need to solve this in your own code, you can use a temp var, such as:
b ||= (x=*(1..3))
Worth mentioning: there's an entirely different use of splat when it's on the left hand side of the expression. This splat is a low-priority greedy collector during parallel assignment.
Examples:
*a, b = [1, 2, 3] #=> a is [1, 2], b is 3
a, *b = [1, 2, 3] #=> a is 1, b is [2, 3]
So this does parse:
*a = (1..3) #=> a is (1..3)
It sets a
to all the results on the right hand side, i.e. the range.
In the rare case that the splat can be understood as either a destructurer or a collector, then the destructurer has precendence.
This line:
x = * y = (1..3)
Evaluates to this:
x = *(y = (1..3))
Not this:
x = (*y = (1..3))
Upvotes: 8