How Keyword Arguments Work in Ruby 3
Ruby 3.0 introduced significant changes to how keyword arguments are handled, aiming to enhance clarity and reduce ambiguity in method calls.
These changes, however, have implications for existing codebases and require developers to adapt their coding practices accordingly.
Understanding Keyword Arguments in Ruby 3
Strict Separation from Positional Arguments
In Ruby 2.x, it was common to pass a hash as the last positional argument, which the method could interpret as keyword arguments. This implicit conversion often led to confusion and unexpected behaviors.
Ruby 3.0 enforces a strict separation between positional and keyword arguments. This means that a hash passed as a positional argument is no longer automatically treated as keyword arguments.
Example:
def greet(name:, age:)
puts "Hello, #{name}. You are #{age} years old."
end
# Ruby 2.x: This would work
greet({ name: "Alice", age: 30 })
# Ruby 3.0: Raises ArgumentError
greet({ name: "Alice", age: 30 })
To pass keyword arguments in Ruby 3.0, you must use the double splat
operator (**
) to explicitly convert a hash into keyword arguments.
Correct Usage in Ruby 3.0:
def greet(name:, age:)
puts "Hello, #{name}. You are #{age} years old."
end
params = { name: "Alice", age: 30 }
greet(**params)
Mandatory Keyword Arguments
Ruby 3.0 allows you to define methods with mandatory keyword arguments by
omitting default values. If a caller omits these arguments, Ruby will raise
an ArgumentError
.
Example:
def configure(host:, port:)
puts "Connecting to #{host} on port #{port}."
end
configure(host: "localhost", port: 3000) # Works
configure(host: "localhost") # Raises ArgumentError
Delegating Arguments
When creating methods that delegate to other methods, itβs essential to forward both positional and keyword arguments explicitly.
Example:
def log_request(*args, **kwargs)
process_request(*args, **kwargs)
end
Alternatively, Ruby 2.7 introduced the ...
syntax for argument forwarding,
which is fully supported in Ruby 3.0.
Example:
def log_request(...)
process_request(...)
end
Common Pitfalls and How to Avoid Them
Implicit Hash to Keyword Argument Conversion
As previously mentioned, Ruby 3.0 does not allow implicit conversion of a hash to keyword arguments. Always use the double splat operator when passing a hash as keyword arguments.
Incorrect:
options = { verbose: true }
run_task(options) # Raises ArgumentError in Ruby 3.0
Correct:
options = { verbose: true }
run_task(**options)
Mixing Positional and Keyword Arguments
Ensure that methods expecting keyword arguments are called with keyword arguments, not positional ones.
Incorrect:
def send_email(to:, subject:)
# ...
end
send_email({ to: "[email protected]", subject: "Welcome" }) # Raises ArgumentError
Correct:
def send_email(to:, subject:)
# ...
end
send_email(to: "[email protected]", subject: "Welcome")
Best Practices
- Be Explicit: Always use the double splat operator when passing hashes as keyword arguments.
- Use Keyword Arguments for Clarity: They make method calls more readable and self-documenting.
- Update Legacy Code: Review and update existing codebases to comply with Ruby 3.0βs keyword argument handling.
- Utilize Argument Forwarding: When delegating methods, use the
...
syntax or explicitly forward both positional and keyword arguments.
Conclusion
Ruby 3.0βs changes to keyword arguments promote clearer and more maintainable code by eliminating ambiguous behaviors. While these changes may require adjustments to existing codebases, adhering to the new conventions will lead to more robust and predictable applications.
Check viewARU - Brand Newsletter!
Newsletter to DEVs by DEVs β boost your Personal Brand & career! π