Bug #8316

Can't pass hash to first positional argument; hash interpreted as keyword arguments

Added by Tyler Rick 12 months ago. Updated 11 months ago.

[ruby-core:54535]
Status:Closed
Priority:Normal
Assignee:Yusuke Endoh
Category:core
Target version:-
ruby -v:ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-linux] Backport:1.9.3: UNKNOWN, 2.0.0: UNKNOWN

Description

I'm able to pass any other type of object to my first argument:

def foo(hash, opt: true)
puts "hash: #{hash}, opt: #{opt.inspect}"
end

foo 'a' # => hash: a, opt: true
foo [{a:1}] # => hash: [{:a=>1}], opt: true
foo [{a:1}], opt: false # => hash: [{:a=>1}], opt: false

But when I try to pass a hash, it raises an ArgumentError:

foo({a:1}) # Raises ArgumentError: unknown keyword: a
# Expected behavior: hash: {:a=>1}, opt: true

I tried to work around the "unknown keyword" error by using ** but ended up getting a "wrong number of arguments (0 for 1)" error instead.

def foowithextra(hash, **extra)
puts "hash: #{hash}, extra: #{extra.inspect}"
end

foowithextra 'a' # hash: a, extra: {}
foowithextra [{a:1}] # hash: [{:a=>1}], extra: {}
foowithextra [{a:1}], opt: false # hash: [{:a=>1}], extra: {:opt=>false}

foowithextra({a:1}) # Raises ArgumentError: wrong number of arguments (0 for 1)
# Expected behavior: hash: {:a=>1}, extra: {}

This behavior is surprising and I haven't seen it mentioned anywhere before. Is it really intentional?


Related issues

Duplicates Backport200 - Backport #8040: Unexpect behavior when using keyword arguments Closed 03/08/2013

History

#1 Updated by Tyler Rick 12 months ago

=begin
http://jp.rubyist.net/magazine/?Ruby200SpecialEn-kwarg#f01 said:

Be careful when passing hashes to methods with both variable length argument lists and keyword arguments.

but in this example, the argument list is ((not)) variable length.
=end

#2 Updated by Matthew Kerwin 12 months ago

TylerRick (Tyler Rick) wrote:

I'm able to pass any other type of object to my first argument:

def foo(hash, opt: true)
puts "hash: #{hash}, opt: #{opt.inspect}"
end

foo 'a' # => hash: a, opt: true
foo [{a:1}] # => hash: [{:a=>1}], opt: true
foo [{a:1}], opt: false # => hash: [{:a=>1}], opt: false

But when I try to pass a hash, it raises an ArgumentError:

foo({a:1}) # Raises ArgumentError: unknown keyword: a
# Expected behavior: hash: {:a=>1}, opt: true

Additionally, calling foo({opt:1}) throws ArgumentError: wrong number of arguments (0 for 1)

This issue can be worked around by calling: foo({a:1},{}) , but it is unexpected.

#3 Updated by Nobuyoshi Nakada 12 months ago

  • Category set to core
  • Status changed from Open to Assigned
  • Assignee set to Yusuke Endoh

#4 Updated by Yusuke Endoh 11 months ago

  • Status changed from Assigned to Closed

This issue was solved with changeset r40992.
Pablo, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


  • vminsnhelper.c (vmcalleesetupkeywordarg,
    vm
    calleesetuparg_complex): consider a hash argument for keyword
    only when the number of arguments is more than the expected
    mandatory parameters. [ruby-trunk - Bug #8040]

  • test/ruby/test_keyword.rb: update a test for above.

Also available in: Atom PDF