The double splat operator **
only seems to work with hashes whose keys are symbols. It will not work when a key is a string, for example. This is true for both ways; for construction:
def foo **; end
foo(:a => 3) #=> nil
foo("a" => 3) #=> ArgumentError: wrong number of arguments (1 for 0)
and destruction:
def bar *; end
bar(**{:a => 3}) #=> nil
bar(**{"a" => 3}) #=> TypeError: wrong argument type String (expected Symbol)
Why is it limited to symbol keys?
It may be related to the fact that named keyword notation a: 3
coincides with the syntax sugar for hash with symbol keys, but as seen above, the double splat works with the ordinary hash notation :a => 3
, so I am not sure about this.
I ran into something like this recently.
If you're in Rails and you have a method that takes keyword arguments and you have a strong params hash that you want to send to it, you can use symbolize_keys
on the params hash and it will properly separate out the arguments, no double splat needed.
class ContactForm
def initialize(name:, email:)
@name = name
@email = email
end
# Other stuff
end
class ContactController < ApplicationController
def send_mail
@contact_form = ContactForm.new(contact_params)
if @contact_form.submit
# Do stuff
end
end
def contact_params
params.require(:contact_form).permit(:name, :email).symbolize_keys
end
end
The short answer: that's how keyword arguments, which the double-splat operator is supposed to capture, are expressed in Ruby.
The long answer: the double-splat operator is for capturing keywords as seen in this answer: https://stackoverflow.com/a/18289218/3644699
Since Ruby 2.0, Ruby supports true keyword arguments. My guess is that in the main implementation they are still represented as Hashes whose keys are symbols, similar to how they were simulated before the language officually supported them.
The particular errors you're getting likely depend on implementation. For example, executing the latter code snippet in irb shows the function that raises the TypeError:
2.1.2 :001 > def bar *; end
=> :bar
2.1.2 :002 > bar(**{"a" => 3})
TypeError: wrong argument type String (expected Symbol)
from (irb):2:in `core#hash_merge_kwd'
from (irb):2
from /home/mkis/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'
2.1.2 :003 >
hash_merge_kwd
is an internal function, defined here: https://github.com/ruby/ruby/blob/d738e3e15533e0f500789faaedcef9ed9ca362b9/vm.c#L2398
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With