🍣
Ruby の位置引数とキーワード引数で混乱したのでメモ
tl;dr
- 位置引数とキーワード引数が厳密に区別され
:
の有無で決定されるので注意する
def param_by_position(param)
# ^
end
def param_by_keywords(param:)
# ^
end
- 2.7.5 と 3.2.0 ではエラータイミングも違うので注意する
起きたこと
- 位置引数とキーワード引数が混在するメソッドで、全てキーワード引数として指定しようとしたら Hash と解釈されて思ってもないエラーになって悩んだ
分かったこと
- 位置引数とキーワード引数は区別され、位置引数の仮引数名を呼び出し時に指定しても、位置引数をキーワードを付きで指定することはできない
- C# だと呼び出し時にキーワードを付与することができるのでここが誤解の元だった
- 位置引数とキーワード引数と区別して(デフォルトパラメータの有無から)必要な引数の個数が管理される
-
- の仕様とあわせて、位置引数の場所に仮引数の名前を付けてキーワード呼び出しをすると、位置引数の個数があわなくなる
-
- 2.7.5 と 3.2.0 では解釈の違いがあるパターンがある
-
(a, b: nil)
という引数を取るメソッドに(a:1,b:1)
を渡したとき- 2.7.5 では呼び出し時の引数を全体として Hash と解釈して、位置引数に渡す
- なので呼び出し時にはエラーにならず、その後の a の利用時に型があわないエラーを引き起こす
- 3.2.0 では呼び出し時の引数を Hash に解釈せず、個数が合わないエラーとなる
- ので、呼び出しエラーとなりエラー原因箇所を特定しやすそう
- 2.7.5 では呼び出し時の引数を全体として Hash と解釈して、位置引数に渡す
-
コード
以下のコードで確認
t.rb
def test1(a:,b:2)
print(a)
print("\n")
print(b)
print("\n"
return
end
def test2(a,b:2)
print(a)
print("\n")
print(b)
print("\n"
return
end
test1(1,2)
test1(a:1,b:2)
test1(1,b:2)
test2(1,2)
test2(a:1,b:2)
test2(1,b:2)
2.7.5 の動作
t.rb(main):019:0> test1(1,2)
Traceback (most recent call last):
5: from /usr/local/bin/irb:23:in `<main>'
4: from /usr/local/bin/irb:23:in `load'
3: from /usr/local/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
2: from t.rb:18
1: from t.rb:1:in `test1'
ArgumentError (wrong number of arguments (given 2, expected 0; required keyword: a))
t.rb(main):020:0> test1(a:1, b:2)
1
2
=> nil
t.rb(main):021:0> test1(1, b:2)
Traceback (most recent call last):
5: from /usr/local/bin/irb:23:in `<main>'
4: from /usr/local/bin/irb:23:in `load'
3: from /usr/local/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
2: from t.rb:21
1: from t.rb:1:in `test1'
ArgumentError (wrong number of arguments (given 1, expected 0; required keyword: a))
t.rb(main):022:0> test2(1,2)
Traceback (most recent call last):
6: from /usr/local/bin/irb:23:in `<main>'
5: from /usr/local/bin/irb:23:in `load'
4: from /usr/local/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
3: from t.rb:21
2: from t.rb:22:in `rescue in irb_binding'
1: from t.rb:9:in `test2'
ArgumentError (wrong number of arguments (given 2, expected 1))
t.rb(main):023:0> test2(a:1, b:2)
{:a=>1, :b=>2}
2
=> nil
t.rb(main):024:0> test2(1, b:2)
1
2
=> nil
3.2.0 の動作
t.rb(main):017:0> test1(1,2)
t.rb:1:in `test1': wrong number of arguments (given 2, expected 0; required keyword: a) (ArgumentError)
from t.rb:17:in `<top (required)>'
from /usr/local/lib/ruby/gems/3.2.0/gems/irb-1.6.2/exe/irb:11:in `<top (required)>'
from /usr/local/bin/irb:25:in `load'
from /usr/local/bin/irb:25:in `<main>'
t.rb(main):018:0> test1(a:1,b:2)
1
2
=> nil
t.rb(main):019:0> test1(1,b:2)
t.rb:1:in `test1': wrong number of arguments (given 1, expected 0; required keyword: a) (ArgumentError)
from t.rb:19:in `<top (required)>'
from /usr/local/lib/ruby/gems/3.2.0/gems/irb-1.6.2/exe/irb:11:in `<top (required)>'
from /usr/local/bin/irb:25:in `load'
from /usr/local/bin/irb:25:in `<main>'
t.rb(main):020:0> test2(1,2)
t.rb:9:in `test2': wrong number of arguments (given 2, expected 1) (ArgumentError)
from t.rb:20:in `<top (required)>'
from /usr/local/lib/ruby/gems/3.2.0/gems/irb-1.6.2/exe/irb:11:in `<top (required)>'
from /usr/local/bin/irb:25:in `load'
from /usr/local/bin/irb:25:in `<main>'
t.rb(main):021:0> test2(a:1,b:2)
t.rb:9:in `test2': wrong number of arguments (given 0, expected 1) (ArgumentError)
from t.rb:21:in `<top (required)>'
from /usr/local/lib/ruby/gems/3.2.0/gems/irb-1.6.2/exe/irb:11:in `<top (required)>'
from /usr/local/bin/irb:25:in `load'
from /usr/local/bin/irb:25:in `<main>'
t.rb(main):022:0> test2(1,b:2)
1
2
=> nil
まとめ
-
:
の有無に注意する - 動作検証コードを書くときにバージョンをあわせるよう注意する
Discussion