Code Fix

上級

キーワード引数を委譲するメソッドで ** を付け忘れると ArgumentError になる

受け取ったキーワード引数(**optionsで受けたハッシュ)をそのまま別のメソッドに渡すには、呼び出し側でも**を付けて展開する必要があります。**を付けずに渡すと、ハッシュが1個の位置引数として渡ってしまい、キーワード引数として認識されずArgumentErrorになります。

エラーメッセージの読み方

main.rb:1:in `configure': wrong number of arguments (given 1, expected 0; required keywords: host, port) (ArgumentError)
main.rb
ファイル名
1
行番号 — 実際にクラッシュした行
configure
発生場所 — <main> ならトップレベル、メソッド名ならそのメソッドの中
ArgumentError
例外クラス — 何が起きたか。ここを検索するのが最短です
wrong number of arguments (given 1, expected 0; required keywords: host, port)
詳細メッセージ — どの値が問題だったか

このエラーが出る典型パターン

パターン1

 1  def configure(host:, port:)
 2    "#{host}:#{port}"
 3  end
 4  
 5  def wrapper(**opts)
 6    configure(opts)  
                   ^
 7  end
 8  
 9  puts wrapper(host: "localhost", port: 3000)
main.rb:1:in `configure': wrong number of arguments (given 1, expected 0; required keywords: host, port) (ArgumentError) from main.rb:6:in `wrapper' from main.rb:9:in `<main>'

**を付けずに渡すとハッシュが1個の位置引数として扱われ、キーワード引数として認識されずエラーになります。

直し方: (opts)(**opts) にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  def connect(host:, timeout:)
 2    "#{host} (#{timeout}s)"
 3  end
 4  
 5  def wrapper(**opts)
 6    connect(opts)  
                 ^
 7  end
 8  
 9  puts wrapper(host: "db", timeout: 5)
main.rb:1:in `connect': wrong number of arguments (given 1, expected 0; required keywords: host, timeout) (ArgumentError) from main.rb:6:in `wrapper' from main.rb:9:in `<main>'

受け取った**optsを次のメソッドに伝えたい場合、呼び出し側でも**で展開する必要があります。

直し方: (opts)(**opts) にします。

この問題を解いてみる →

パターン3

 1  def setup(mode:, level:)
 2    "#{mode}/#{level}"
 3  end
 4  
 5  def wrapper(**opts)
 6    setup(opts)  
               ^
 7  end
 8  
 9  puts wrapper(mode: "auto", level: 3)
main.rb:1:in `setup': wrong number of arguments (given 1, expected 0; required keywords: mode, level) (ArgumentError) from main.rb:6:in `wrapper' from main.rb:9:in `<main>'

**を付けずに渡すとただのハッシュという1個の引数として扱われ、キーワード引数を期待する側と噛み合いません。

直し方: (opts)(**opts) にします。

この問題を解いてみる →

パターン4

 1  def build_url(scheme:, path:)
 2    "#{scheme}://#{path}"
 3  end
 4  
 5  def wrapper(**opts)
 6    build_url(opts)  
                   ^
 7  end
 8  
 9  puts wrapper(scheme: "https", path: "example.com")
main.rb:1:in `build_url': wrong number of arguments (given 1, expected 0; required keywords: scheme, path) (ArgumentError) from main.rb:6:in `wrapper' from main.rb:9:in `<main>'

キーワード引数だけを受け取るメソッドに、展開していないハッシュを渡すと引数の形が一致しません。

直し方: (opts)(**opts) にします。

この問題を解いてみる →

パターン5

 1  def register(name:, plan:)
 2    "#{name}/#{plan}"
 3  end
 4  
 5  def wrapper(**opts)
 6    register(opts)  
                  ^
 7  end
 8  
 9  puts wrapper(name: "taro", plan: "pro")
main.rb:1:in `register': wrong number of arguments (given 1, expected 0; required keywords: name, plan) (ArgumentError) from main.rb:6:in `wrapper' from main.rb:9:in `<main>'

*と**は見た目が似ていますが役割が違います。位置引数の展開とキーワード引数の展開を混同しないよう注意してください。

直し方: (opts)(**opts) にします。

この問題を解いてみる →

よくある誤解

「**optionsで受け取った値は、そのまま書けばキーワード引数として次のメソッドに伝わるはず」という思い込みは誤りです。**を付けずに渡すとただのハッシュという位置引数1個として扱われるため、受け取り側がキーワード引数を期待しているとエラーになります。

まとめ

キーワード引数を委譲するメソッドで ** を付け忘れると ArgumentError になるは上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)