Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Space before star breaks syntax in ruby multiplication statement

While adding some whitespace to make code more readable (line up with code above it), I came across this:

class C
  def x
    42
  end
end
m=C.new

Now this will give "wrong number of arguments":

m.x *m.x

And this will give "syntax error, unexpected tSTAR, expecting $end":

2/m.x *m.x

What exactly is happening in the parser here?

I tested with Ruby 1.9.2 and 2.1.5.

like image 946
Trygve Flathen Avatar asked Dec 15 '14 14:12

Trygve Flathen


2 Answers

* is used for both an operator (42 * 42) and argument unpacking (myfun *[42, 42]).

When you do:

m.x *m.x  
2/m.x *m.x

Ruby interprets this as argument unpacking, rather than the * operator (ie. multiplication).

In case you're not familiar with it, argument unpacking (sometimes also called "splat", or "splats") means that you can have a function like this:

def myfun arg1, arg2; end

And call it like this:

myfun(*['Hello', 'World'])

arg1 is set to Hello, and arg2 is set to World.

I believe the rules are to determine which to use is:

  • Space before but not after a * -> Argument unpacking
  • Start of function parenthesis -> Argument unpacking
  • Everything else -> Multiplication (or rather, the * operator, since Ruby does operator overloading).

Good guidelines are:

  1. Use the "optional" function parenthesis when you intend argument unpacking;
  2. use spaces before and after * when you intend the * operator (multiplication).

Ruby will actually warn you about this when you run ruby -v:

test.rb|11 warning| `*' interpreted as argument prefix                                    
test.rb|12 warning| `*' interpreted as argument prefix
like image 63
Martin Tournoij Avatar answered Oct 21 '22 07:10

Martin Tournoij


In simple language:

When you say

m.x *m.x

It internally calls m.x(*m.x) i.e it considers *m.x as splat argument to m.x.

Since, there is an x method defined on m which takes any argument(s), you are getting the "wrong number of arguments" error.

When you call

m.x * m.x

It considers * as a method of x which takes an object of type 'Fixnum' and in Ruby, the * method that takes an argument is defined for 'Fixnum' class. So, it's the same as calling

m.x.*(m.x)

and hence it works!

I hope that helped you understand the problem, in case any clarification is required, feel free to comment.

like image 3
Shweta Avatar answered Oct 21 '22 08:10

Shweta