The "*" operator on an array in that context, turns an array into a list of arguments.
Let's say you have a method that takes three arguments:
def takes_three(one, two, three)
puts "first: #{one}, second: #{two}, third: #{three}"
end
If you tried to do this:
arr = ["a", "b", "c"]
takes_three(arr)
# => ArgumentError: wrong number of arguments (1 for 3)
Your method takes three arguments, but you only passed it one -- that one was an array.
If you want to turn the array of three elements into three separate arguments to the method, that's what *
does here:
takes_three(*arr)
# => puts "first: a, second: b, third: c"
What gets confusing in your examples, believe it or not, is your use of p
, shortcut in irb for puts
. puts
takes a variable number of arguments, you can give it however many you want. It will output them all, separated by newlines. And if you give it a single array argument, it will still output each element in the array separated by newlines.
irb(main):068:0> puts ["a", "b", "c"]
a
b
c
irb(main):069:0> puts "a", "b", "c"
a
b
c
irb(main):070:0> puts arr
a
b
c
irb(main):071:0> puts *arr
a
b
c
Since puts "a", "b", "c"
and puts ["a", "b", "c"]
do the same thing, puts arr
and puts *arr
do the same thing too. Since puts *arr
is equivalent to puts 1, 2, 3
, it expands an array into separate arguments for the method.
So your attempt to investigate with puts
makes it confusing, because puts
takes a variable number of arguments, and does the same thing with one argument that's an array of n elements, as it does with n separate arguments each of those elements. Most methods don't work that way though, so for most methods *
, changing the nature of the arguments you're giving to the method, will also change what the method does. It is useful for a kind of indirection, where you build up the arguments you want to pass in an array dynamically, instead of writing them fixed in source code.