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.
Splat operator or start (*) arguments in Ruby define they way they are received to a variable. Single splat operator can be used to receive arguments as an array to a variable or destructure an array into arguments. Double splat operator can be used to destructure a hash.
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.
The * is the splat operator. It expands an Array into a list of arguments, in this case a list of arguments to the Hash.
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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With