[Talking-Ruby] Positional and keyword arguments in Ruby

Positional arguments, optional arguments, and keyword arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# a is an positional argument.
def positional_method(a); end

# b is an optional argument, defaults to empty hash.
def optional_method(b = {}); end

# c is an keyword argument, defaults to nil.
# r is a required keyword argument.
def keyword_argument_method(c: nil, r:); end

# d is an optional splat argument.
def optional_splat(*d); end

# e is an optional double splat argument.
def optional_double_splat(**e); end

Splat Operators

* is called splat, and ** is called double splat. You can use these operators in method signatures and method calls:

In method signatures

  • Single splat (*) packs the argument into Array.

  • Double splat (**) packs the argument into Hash.

1
2
3
4
5
6
7
8
9
10
11
12
13
def a_method(*args)
p args
end

def another_method(**args)
p args
end

a_method("a", "b")
# => ["a", "b"]

another_method(a: "a", b: "b")
# => {:a=>"a", :b=>"b"}

In method calls

  • Single splat (*) unpacks the argument from Array.

  • Double splat (**) unpacks the argument from Hash.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def a_method(a, b)
p "a = #{a}, b = #{b}"
end

def another_method(a:, b:)
p "a = #{a}, b = #{b}"
end

a = ["a", "b"]
a_method(*a)
# => "a = a, b = b"

b = {a: "a", b: "b"}
another_method(**b)
# => "a = a, b = b"

Keyword Arguments

Keyword arguments first introduced in Ruby 2.0 - https://www.ruby-lang.org/en/news/2013/02/24/ruby-2-0-0-p0-is-released/. Block keyword arguments also introduced:

1
2
3
4
5
6
7
8
9
# Keyword arguments first introduced in Ruby 2.0.
def translate(key: "en")
puts key
end

# Required keyword argument added in Ruby 2.1.
def required_translate(key:)
puts key
end

Ruby < 2.0

Before Ruby 2, we use options Hash to achieve keyword arguments, and Hash#fetch for required keyword arguments:

1
2
3
4
5
6
7
8
9
10
def ruby_19_translate(options = {})
puts options.fetch(:key, "en")
end

def ruby_19_required_translate(options = {})
puts options.fetch(:key)
end

ruby_19_translate # => "en"
ruby_19_required_translate # KeyError: key not found: :key

Automatic Conversion

Ruby < 2.7

If the last argument of a method call is a Hash, Ruby < 2.7 will automatically convert to Keyword Arguments.

1
2
3
4
5
6
7
def method_last_hash(a:)
p a
end

# Ruby < 2.7 will automatically convert to Keyword Arguments
method_last_hash({a: 'a'})
# => "a"

Ruby 2.7

Upgrade to Ruby 2.7 and fix the places rely on such automatic conversion.

  • Be explicit. Wrap argument in {} if you want to pass in a hash:
1
2
3
4
5
6
7
8
9
def translate(key: "en")
puts key
end

translate({ key: value })

# instead of

translate(key: value)
  • Prefix argument with ** if you want to pass in keywords:
1
2
3
4
5
6
7
8
9
10
11
def translate(key: "en")
puts key
end

keywords = { throw: true, raise: false }

translate(**keywords)

# instead of

translate(keywords) #

Use double splat keyword arguments instead of options hash:

1
2
3
4
5
6
7
8
9
def good(**kwargs)
kwargs
end

# instead of

def old(kwargs = {})
kwargs
end

Support for **nil

Ruby 2.7 added support for **nil to explicitly mention if a method doesn’t accept any keyword arguments in method call.

1
2
3
4
5
6
def sum(a, b, **nil)
a + b
end

sum(2, 3, x: 4)
=> ArgumentError (no keywords accepted)

To suppress above deprecation warnings we can use -W:no-deprecated option.

References

[1] Ruby Keyword Arguments | Online Video Tutorial by thoughtbot - https://thoughtbot.com/upcase/videos/ruby-keyword-arguments

[2] Ruby 2.7 deprecates conversion of keyword arguments | BigBinary Blog - https://www.bigbinary.com/blog/ruby-2-7-deprecates-conversion-of-keyword-arguments

[3] Ruby 3 Keyword Arguments — Juanito Fatas - https://juanitofatas.com/ruby-3-keyword-arguments

[4] Ruby 2.7 deprecates automatic conversion from a hash to keyword arguments | Saeloun Blog - https://blog.saeloun.com/2019/10/07/ruby-2-7-keyword-arguments-redesign.html

[5] Separation of positional and keyword arguments in Ruby 3.0 - https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/