Keyword arguments and forwarding

Required and optional keywords

A keyword argument is named at the call site, so the reader knows what each value means without opening the method. A keyword with no default is required; a keyword with a default is optional:

def greet(name:, greeting: "Hello") "#{greeting}, #{name}" end puts greet(name: "Ada") puts greet(name: "Ada", greeting: "Hi")

ruby greet_demo.rb

Hello, Ada
Hi, Ada

Calling without the required keyword raises before the method body runs, and the message names the missing one:

ruby -e 'def f(a:) = a; f'

-e:1:in 'f': missing keyword: :a (ArgumentError)
  from -e:1:in '<main>'

Collecting the rest with **

**rest collects the keywords the method does not name, which lets a wrapper pass options through without listing them:

ruby -e 'def f(a:, b: 2, **kw) = [a, b, kw]; p f(a: 1, c: 3)'

[1, 2, {c: 3}]

The result is an array of the two named values and a hash of the rest. Ruby 3.4 prints the hash in the short form, {c: 3}, the same change lesson 34, later in the course, showed for p.

A trailing hash and a keyword call are the same thing at the call site in Ruby 3: f(a: 1) passes keywords. The difference appears at the definition: def f(opts = {}) takes a positional hash, and a caller that passes keywords to it gets an ArgumentError in Ruby 3 unless the hash is wrapped in braces.

Forwarding with ...

A method that adds a step and calls another one should forward everything it received: positionals, keywords and the block. ... does exactly that, and it is shorter than spelling out all three:

def inner(a, b:) a + b end def outer(...) inner(...) end puts outer(1, b: 2)

ruby -e 'def inner(a, b:) = a + b; def outer(...) = inner(...); p outer(1, b: 2)'

3

Before Ruby 2.7 the same wrapper needed def outer(*args, **kwargs, &block) and a call with three splats. ... is the version to write now, and it also forwards a block the wrapper never names.

What to watch for

A keyword and a positional argument with the same name are two different parameters. def f(x, x:) is legal and confusing; rename one of them.

A method with many optional keywords is a configuration object in disguise. When the list passes five, a small class or a Data value with defaults is easier to validate and to read.

Next: Blocks, yield and closures

Article author: Arthur Isaev