2 min read

Ruby's Anonymous Parameter Forwarding

Summarising argument forwarding
Ruby's Anonymous Parameter Forwarding
Photo by Tarik Haiga / Unsplash

Ruby has some anonymous argument forwarding syntax. This post summaries them, but Victor Shepelev has a great post going into detail that is well worth a read.

Positional Argument Forwarding

Forward all positional arguments directly to another method without allocating a local variable in the forwarding method.

def foo(x, y)
  pp [x,y]
end

def bar(*)
  foo(*)
end

bar(1,2)

bar will forward all positional parameters to foo, foo will check the arity of what is received.

Named Argument Forwarding

Forward all named arguments directly to another method without allocating local variable in the forwarding method.

def foo(x:, y:)
  pp [x, y]
end

def bar(**)
  foo(**)
end

bar(x: 1, y: 2)

Block Forwarding

Forward a block directly to another method avoiding the block → proc → block conversion which is expensive.

def foo(&block)
  yield if block_given?
end

def bar(&)
  foo(&)
end

bar { puts 'Hello world' }

Combinations of all

The positional, named, and block parameters can be forwarded separately (or together) using a combination of the above.

def foo(x, y)
  pp [x, y]
end

def bar(x:, y:)
  pp [x,y]
end

def baz(&block)
  yield if block_given?
end

def qux(*, **, &)
  foo(*)
  bar(**)
  baz(&)
end

All Argument Forwarding

Forward all arguments directly.

def foo(a, b, x:, y:, &block)
  pp [a, b, x, y]
  yield if block_given?
end

# yes, 3 dots, this is not an omission
def bar(...) 
  foo(...)
end

Curiosities

Prevent any named parameters

Ruby does some auto packing of hashes as the last parameter to methods white positional arguments. This was a pseudo way of using named parameters before their formal introduction.

def foo(values)
  pp values
end

foo(x: 1, y: 2)

#=> {:x=>1, :y=>2}

There is only argument, the parameters get packed into a hash. This was quite a common mechanism for receiving so-called options hashes but should be considered a legacy approach.

To avoid any confusion, it is possible to explicitly declare a method will not take anything that might be confused as a named parameter.

def foo(x, **nil)
  pp x
end

foo(x: 1)

# => no keywords accepted (ArgumentError)

Blocks

Blocks do not allow argument forwarding at all.