Method Arguments In Ruby

ArgumentMethod arguments in Ruby are interesting because of the great flexibility in how you’re allowed to supply them to methods. Ruby method arguments can loosely be broken up into two categories, required arguments and optional arguments. However, I like to break them up into three categories (I hope it will become clear by the end of this post why I do so):

  • required arguments
  • arguments with default values
  • optional arguments

Required Arguments

These are just your stock standard method arguments, e.g.:

def some_method(a, b)
end

To call the method above you will need to supply two arguments to the method call, e.g.:

some_method(25,"hello")

Pretty basic stuff, nothing much to see here, moving on :).

Arguments With Default Value

In Ruby you can supply a default value for an argument. This means that if a value for the argument isn’t supplied, the default value will be used instead, e.g.:

def some_method(a, b, c=25)
end

You may call the method above in one of two ways:

some_method(25,"hello")

or

some_method(25,"hello", 48)

In the first case you don’t supply a value for the third parameter, so it’s default value (i.e. 25) will be used in the method body. In  the second case you do supply a value, so it will be used in place of the default value. Therefore, arguments with default values are a type of optional argument.

Optional Arguments

If you want to decide at runtime how many – if any – arguments you will supply to a method, Ruby allows you to do so. You need to use a special notation when you define the method, e.g.:

def some_method(*p)
end

You can call the above method with any number of arguments (including none), e.g.:

some_method

or

some_method(25)

or

some_method(25,"hello", 45, 67)

All of those will work. If no arguments are supplied, then p will be an empty array, otherwise, it will be an array that contains the values of all the arguments that were passed in.

So, far it is all pretty basic stuff. The real fun begins when you need to mix and match the three types of arguments together.

Mixing And Matching The Various Types Of Arguments

What happens when you start to mix required arguments with optional arguments? Do they have to be in any specific order, and what gets assigned to what?

The easiest case is mixing a number of required arguments with the fully optional argument (i.e. the * notation), e.g.:

def some_method(a, b, *p)
end

You can call the above method with two or more values. The first two values will be assigned to arguments a and b, the rest will be assigned to p as an array, pretty simple. But, what if I wanted to do the following:

def some_method(a, b, *p, q)
end

In this case, you can call the above method with 3 or more values. When you call the above method, the required arguments get assigned first and if there are still any values left over they get assigned as an array to the optional argument, e.g.:

some_method(25,35,45,55) - a=25, b=35, p=[45], q=55
some_method(25,35,45) - a=25, b=35, p=[], q=45
some_method(25,35,45,55,65,75) - a=25, b=35, p=[45,55,65], q=75

Notice that the required arguments get assigned the value that corresponds to their order in the argument list, while the optional argument gets all the values that are left over that correspond to it’s order in the list.

Things can get even more involved if we introduce arguments with default values:

def some_method(a, b, c=5, *p, q)
end

In this case you can still call the above method with three or more values. When you make a call, all required arguments must get a value assigned, if there are more values left over, then the arguments with default values will get a value assigned to them, after that if there is still something left over, the optional argument will get those values as an array, e.g.:

some_method(25,35,45) - a=25, b=35, c=5, p=[], q=45
some_method(25,35,45,55) - a=25, b=35, c=45, p=[], q=55
some_method(25,35,45,55,65) - a=25, b=35, c=45, p=[55], q=65
some_method(25,35,45,55,65,75) - a=25, b=35, c=45, p=[55,65], q=75

Once again all arguments get assigned the values that correspond to their order in the argument list. And the arguments with a default value will get something assigned to them (if possible) before the fully optional argument gets any values.

It’s all pretty cool and it might seem like you can do anything with argument lists in Ruby, but there are some things to look out for. The only real hard and fast rule to remember is when you’re mixing optional parameters and default value parameters in the argument list. In this case, all default value parameters must occur before the optional parameter in the list, e.g.:

def some_method(a, b=5, *p) - correct
def some_method(a, *p, b=5) - incorrect!!!

If your optional parameter occurs before your default one, it is a syntax error, which makes sense if you think about it. And obviously, it makes no sense to have two optional arguments in an argument list (i.e. two parameters with * notation).

Feel free to leave a comment if you know of any other interesting things or caveats when it comes to Ruby method arguments.

Image by Anders V

  • Korny

    You should also list the common idiom of named parameters (using a hash map).
    I regularly write code like:
    def foo(params = {})
    {
    params = {:name => “default”}.merge(params)
    … do stuff with params ..
    }
    Then you can call foo() or foo(:name => “fred”} or foo(:size => 666, :name => “barney”).

    • http://www.skorks.com Alan Skorkin

      That’s an excellent tip, certainly worth keeping in mind.

  • jnstq

    I have also seen

    def render(*)
    end

    I quess it just swallow the arguments

    • http://www.skorks.com Alan Skorkin

      Yeah that one should sponge up all the arguments, although I am not sure how you would refer to any of them, maybe there is a built in global of some sort?

    • http://slawosz.github.com/ Sławosz

      It is usefull when you override method:

      class A
      def render(a,b,*args,&blk)
      #…
      end
      end

      class B < A
      def render(*)
      # do something without arguments
      end
      end

      So you dont need to rewrite arguments signature in overrided method. Sometimes usefull.

      PS. Nice article!

  • http://jimneath.org Jim Neath

    Awesome run down.

  • http://dicioccio.fr/ FrihD

    An interesting special argument is the block.
    Also, when you start mixing with super, the blocks, and * arguments you can have really strange behaviours. Have fun !

    • http://www.skorks.com Alan Skorkin

      Yeah I thought I’d leave blocks, for another time, they probably deserve a post of their own.

      You’re right about super though, I’ve just been reading about it the other day, very interesting, I might play around with it and * arguments and write it up if I find anything of interest (which I probably will :)).

  • Guoliang Cao

    Nice writeup! Does this apply to both 1.8 and 1.9 ?

    • http://www.skorks.com Alan Skorkin

      Unfortunately no, this true only for 1.9. As one of the comments below pointed out, 1.8 has a much more rudimentary way it deals with method arguments. For example you won’t be able to have the argument sponge (the * argument), between two required arguments, it needs to be last.

      • http://gfxmonk.net Tim

        BTW, it’s called the splat operator. It’s also mighty handy in certain metaprogramming situations, where you can use the reverse-splat operator to turn a list into an argument list.
        i.e:
        foo(a, b, c)
        is equivalent to
        foo(*[a, b, c])

        allowing you to manipulate argument lists as arrays before sending them to some other function. It’s amazing how useful this can be in practice.

        • http://www.skorks.com Alan Skorkin

          I’ve heard it referred to as a sponge operator before as well :).

          That one sounds like a really useful trick though, I wasn’t aware you could do that, thanks for sharing it.

  • http://entp.com Jeremy

    Note that some of these won’t work (specifically the “def some_method(a, b, c=5, *p, q)”) in Ruby 1.8. 1.9 has changed the way a lot of things are parsed, and I think, in this case, fixed some weird quirks.

    • http://www.skorks.com Alan Skorkin

      Yes 1.8 is definitely a lot less advanced in how it handles method arguments even the location of default value arguments matters (they have to be last I believe) otherwise you get weird behavior depending on how many parameters you pass in.

  • Pingback: Paramètres de méthode en Ruby « 29 minutes par jour : un blog anti-procrastination

  • http://www.innovationontherun.com Rob Di Marco

    Very nice writeup and good comments. Can’t wait for the next post on arguments with blocks!

    • http://www.skorks.com Alan Skorkin

      I will probably write that one up within the next few weeks, I was thinking of including blocks and hashes :).

  • Pingback: Plough through Ruby

  • http://www.jgsbws.com Terry Smith

    Grr.

    This whole thing is a system for generating errors. It doesn’t do a damn thing a hash doesn’t do better. The *p thing should be removed from Ruby.

    • http://www.skorks.com Alan Skorkin

      I agree that if you go overboard with this it is too confusing to be worth it. It is however a good idea to be aware of how the language will behave if you run up against it (hence the post).

      The * argument is a good thing to have if you just want to use it by itself I believe. I certainly wouldn’t be mixing things up like this (the more complex examples in the post) in real code.

      I agree with you, hashes can provide a lot of clarity as Korny pointed out in the first comment.

  • Sam

    What version of ruby are you using for these examples?

    I’ve tried your def some_method(a, b, *p, q) with several versions of 1.8 and it doesn’t work.

    • http://www.skorks.com Alan Skorkin

      Yeah as I explained above, the more involved examples will work only with 1.9 as 1.8 handles arguments more simply. So use 1.9 if you want to try these out :).

  • roger

    also check out http://github.com/maca/arguments very cool

    • http://www.skorks.com Alan Skorkin

      That does look pretty cool, cheers for that.

  • Pingback: The Rubyist: August 2009 Edition

  • http://blog.mostof.it/ ochronus

    Nice article, very informative!
    I’ve also written a quick intro in the topic: http://blog.mostof.it/posts/why-ruby-part-three-method-arguments/

  • Pingback: Saw some good blogs on RoR « Raymond Gao’s blog

  • http://www.israelunique.com Michael Nissim

    I find your site, and this article too, very useful, readable and pleasant. I like the funny pictures of “arguments” you put too! May God bless you!

  • Ramya

    Great post, Helped in getting started

  • Roger Grey

    Hey Alan,

    You wrote that an optional parameter always has to come after default value parameters:
    def some_method(a, b=5, *p) – correct
    def some_method(a, *p, b=5) – incorrect!!!

    From what you wrote, my understanding is that the arguments are assigned first to the required values, then the default values, and finally the rest of the values are passed to the optional argument as an array.

    I think I’m missing something, but I didn’t understand why it makes sense for one version to work and the other to fail, if the values are assigned in that order. (Wouldn’t the default values be dealt with before we get to the optional argument? Or is this purely due to the way the Ruby parser is implemented?)

    Thanks in advance for helping!

  • samar

    liked ur post very much! its nice one. thanks!

  • http://twitter.com/ohforfs Kevin McCaughey

    Thanks! like Roger Gray though, I also do not understand why it should be self evident that (in your last example) default parameters cannot follow optional ones. Could you explain this please? I can’t think of a logical reason – but if it is just because this is the way things are done then I can live with that also :)

    • wxcvbn

      This is because if you have :
      > some_method(‘a’, ‘a1′, ‘a2′, ‘a3′)
      you can’t decide if ‘a2′ is the last argument of *p (and thus, ‘c’ will be initialized with 5) or if it’s the c (and thus, c will be ‘a3′)

  • Pingback: Ruby params | Syr21

  • Daniel

    What a beautiful post. Helps a lot. Thanks.

  • Pingback: default argument value & nil guard | chérie & code

  • Pingback: RubySource | Method Signature You Can Sink Your Teeth IntoRubySource