/ Tags: RUBY 3 / Categories: RUBY

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.

cdrrazan

Rajan Bhattarai

Software Engineer by work! πŸ’» 🏑 Grad. Student, MCS. πŸŽ“ Class of '23. GitKraken Ambassador πŸ‡³πŸ‡΅ 2021/22. Works with Ruby / Rails. Photography when no coding. Also tweets a lot at TW / @cdrrazan!

Read More