Ruby 2.7: ハッシュからキーワード引数への自動変換が非推奨に(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

なお、コード例には翻訳時に補助的なコメントを追加しています。

Ruby 2.7: ハッシュからキーワード引数への自動変換が非推奨に(翻訳)

Rubyのメソッド定義は極端なまでに柔軟です。それを端的に表す例がMarc-André Lafortuneのブログ記事にありましたので引用します。

class C
  def hi(needed, needed2,
         maybe1 = "42", maybe2 = maybe1.upcase,
         *args,
         named1: 'hello', named2: a_method(named1, needed2),
         **options,
         &block)
  end
end

C.instance_method(:hi).parameters
# => [ [:req, :needed], [:req, :needed2],
#      [:opt, :maybe1], [:opt, :maybe2],
#      [:rest, :args],
#      [:key, :named1], [:key, :named2],
#      [:keyrest, :options],
#      [:block, :block] ]

Ruby 2.7ではキーワード引数の設計の変更に踏み切ります。しかしその前にキーワード引数について理解しておきましょう。

そもそもキーワード引数とは?

キーワード引数はRuby 2.0で導入され、ちょうどオプション引数(つまりハッシュオブジェクト{ })と同じように扱われていました。キーワード引数を受け取るメソッドにオプション引数を渡すこともできますし、逆にオプション引数を受け取るメソッドにキーワード引数を渡すこともできます。

# Ruby 2.6まで
def a_method(k: 1)  # キーワード引数を受け取るメソッド
  puts "k: #{k}"
end

a_method
=> k: 1
a_method(k: 2)       # キーワード引数を渡せる
=> k: 2
a_method({k: 2})     # オプション引数も渡せる
=> k: 2

def a_method(**kw)   # オプション引数を受け取るメソッド
  puts "kw, #{kw}"
end

a_method
=> kw, {}
a_method(k: 2)       # キーワード引数も渡せる
=> kw, {:k=>2}
a_method({k: 2})     # オプション引数を渡せる
=> kw, {:k=>2}

#14183 — “Real” keyword argument」で指摘されているとおり、キーワード引数とオプション引数のどちらを渡してもよいという互換性は、実に多くのバグやエッジケースの温床となっていました。

RubyConf 2017では、Matzが「Ruby 3.0で『本物の』キーワード引数をお見せします」と公式にアナウンスしました(動画↓)。キーワード引数が通常の引数から完全に切り離されるのもそのひとつです。

この変更によって非互換性が生じます。Ruby 3.0でのキーワード引数の設計見直しに向けた準備のため、ハッシュをキーワード引数に自動変換する挙動はRuby 2.7で非推奨となります。

本記事では、この変更によって実際に影響を受ける可能性のあるコードと、Ruby 3.0をサポートするための移行方法について記します。

キーワード引数を受け取るメソッドにハッシュが渡される場合

# Ruby 2.7
def a_method(k: 1)    # キーワード引数を受け取るメソッド
  puts "k: #{k}"
end

a_method({k: 1})      # ハッシュを渡すとwarningが表示される
(irb):4: warning: The last argument is used as the keyword parameter
(irb):1: warning: for 'a_method' defined here
# => k: 1

# warningを回避し、かつRuby 3での互換性を確保するには
# double splat演算子を付けて渡す

a_method(**{k: 1})
# => k: 1

オプション引数とキーワード引数を両方受け取るメソッドに、渡した引数が足りない場合

# Ruby 2.7
def a_method(opts, **kw)
  puts "opts, #{opts}"
  puts "kw, #{kw}"
end

a_method(k: 1)    # キーワード引数だけを渡すとwarningが表示される
(irb):5: warning: The keyword argument is passed as the last hash parameter
(irb):1: warning: for 'a_method' defined here
# => opts, {:k=>1}
kw, {}

# warningを回避し、かつRuby 3での互換性を確保するには
# 引数をハッシュオブジェクトにする

a_method({k: 1})   # ハッシュオブジェクト { } ならOK
=> opts, {:k=>1}
=> kw, {}
# メソッド定義に**nilと書くことで
# メソッドがキーワード引数を受け取らないことを明示できる
def a_method(opts, **nil)
  puts "opts, #{opts}"
end

a_method(k: 1)
#=> ArgumentError (no keywords accepted)

互換レイヤ

互換レイヤとして、オプション引数を受け取るメソッドにキーワード引数を渡すことは引き続き許容されます。

# Ruby 2.6と2.7
def a_method(opts={})   # オプションハッシュを受け取るメソッド
  puts "opts, #{opts}"
end

a_method(k: 1)          # これはOK
#=> opts, {:k=>1}

非シンボルがキーワード引数のキーとして使えるようになる

任意のキーワードを(double splat演算子で)受け取れるメソッドには、キーがシンボルでないキーワード引数を渡すことが許容されるようになります。

Ruby 2.6まで

def a_method(**kw)      # double splatで任意のキーワードを受け取るメソッド
  puts "kw, #{kw}"
end

a_method("k" => 1)      # キーが非シンボルだとエラーになる
#=> ArgumentError: wrong number of arguments (given 1, expected 0)
from (pry):7:in 'a_method'

a_method(**{"k" => 1})  # { }で囲んでも**を付けてもダメ
#=> TypeError: wrong argument type String (expected Symbol)
from (pry):11:in `__pry__'

Ruby 2.7

def a_method(**kw)  # double splatで任意のキーワードを受け取れる
  puts "kw, #{kw}"
end

a_method("k" => 1)  # 非シンボルキーを渡せる
=> kw, {"k"=>1}

まとめ

Ruby 2.7ではハッシュからキーワード引数への自動変換が非推奨になります。さまざまなシナリオからわかるように、アプリケーションでこの非推奨の用法から移行することをまず検討しましょう。今後も改善が続く部分につき、今後のRubyではさらに変更が行われるかもしれません。

おたより発掘

Ruby 2.7: ハッシュからキーワード引数への自動変換が非推奨に(翻訳)

山岡節だw

2019/11/01 12:09

関連記事

Ruby 2.7.0-preview2がリリース

Ruby2.5.xのパラメータの制約についてまとめてみた

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好きで、Goで書かれたRubyライクなGoby言語のメンテナーでもある。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

夏のTechRachoフェア2019

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ