DEV Community

Donald Dong
Donald Dong

Posted on

Ruby Keyword Arguments

In Ruby 2, keyword arguments are essentially the same as a hash object at the end of the positional arguments.

Ruby 2.x:

def test(*args)
  p args
end
test(1, {y: 2}) # [1, {:y=>2}]
test(1, y: 2) # [1, {:y=>2}]
Enter fullscreen mode Exit fullscreen mode

Ruby 2.x:

def test2(hash={}, **kwargs)
  p [hash, kwargs]
end
test2({y: 2}) # [{}, {:y=>2}]
test2(y: 2) # [{}, {:y=>2}]
Enter fullscreen mode Exit fullscreen mode

Though I have passed in a hash, it's actually picked up by the kwargs.
This... is probably not something one would expect, right?


So, in Ruby 3, keyword arguments are completely separated from the normal arguments (learn more here).

Ruby 3.0.0:

def test2(hash={}, **kwargs)
  p [hash, kwargs]
end
test2({y: 2}) # [{:y=>2}, {}]
test2(y: 2) # [{}, {:y=>2}]
Enter fullscreen mode Exit fullscreen mode

If a method has explicit keyword arguments in Ruby 3, all the callers must explicitly pass in keyword arguments (or explicitly unpack a hash as the keyword args) in order to supply the keyword args.

Using this standard, one could write Ruby code that's compatible with both Ruby 2 and Ruby 3.

Caveat

There’s a catch in writing Ruby 2 and Ruby 3 compatible code though:

Ruby 2.x:

def foo; end
a = []
b = {}
foo(*a, **b) # this is okay
send(:foo, *a, **b)  # this is not okay: ArgumentError (wrong number of arguments (given 1, expected 0))
Enter fullscreen mode Exit fullscreen mode

There is a bug in Ruby 2 that is causing issues when unpacking empty hashes for kwargs (fixed in Ruby 3). In some cases, we’d have to conditionally unpack the kw hashes in Ruby 2 so that the code works with both Ruby versions (unpacking empty kw hashes causes errors sometimes). As a result, the actual code would look a bit ugly (with an if statement to unpack only if the kw hash is not empty), unfortunately 😅.

Another option is to use https://github.com/ruby/ruby2_keywords but it's better to actually fix the keyword argument passing when possible.

Top comments (2)

Collapse
 
jfoo1984 profile image
Jerry Fu • Edited

Do you have an example of "explicitly unpack a hash as the keyword args"? Also, do you have a link or documentation about the explicit keyword required in all callers? I think I am running into the issue, but I haven't been able to find much else about this.

Collapse
 
konami99 profile image
Richard Chou • Edited

In method calls, double splat unpacks the argument from Hash.
So method_call(**hash) will unpack hash arguments.

Source: juanitofatas.com/ruby-3-keyword-ar...