こんにちは!oda@エンジニア1年目です!
先日、Ruby2.7からRuby3.0での変更点について調べていました。
今回は、その中でもバージョンアップ時にエラーを引き起こす原因となる「キーワード引数と通常の引数の分離」をテーマにしたいと思います。
バージョンアップ時の対応
Ruby2.7でキーワード引数と通常の引数(具体的にはハッシュ)の間の自動変換が非推奨となり、警告が出るようになりました。
さらにRuby3.0では、キーワード引数と通常の引数が完全に分離され、警告ではなくエラーが出るようになりました。
上記変更は、キーワード引数として渡したいハッシュにdouble splat演算子(**
)を足すことで、Ruby3.0でも同じようにアプリケーションを動かすことができます。
私も Ruby2.7へのバージョンアップ時には、キーワード引数として扱いたいハッシュに**
を足すという対応をしました。
Ruby3.0への変更点を調べる中で、再び「キーワード引数と通常の引数の分離」に出会ったので、今回は、その背景まで見てみることにしました。
変更の背景
変更の背景は、「Ruby 3.0における位置引数とキーワード引数の分離について」(※)に詳しく書かれていました。
(以下、掲載のコードは※から引用しています。)
自動変換が非推奨・エラーに変わったのは、オプション引数とキーワード引数をどちらも受け取るメソッドでは混乱を招くケースがあるということが原因のようです。
オプション引数とは、デフォルト値を設定した引数のことを言います。
記事には、混乱を招くケースとして以下のコードが例示されていました。
def foo(x, **kwargs) p [x, kwargs] end def bar(x=1, **kwargs) p [x, kwargs] end foo({}) #=> [{}, {}] bar({}) #=> [1, {}] bar({}, **{}) #=> 期待は: [{}, {}]だが実際はl: [1, {}]
これは、第1引数の{}
が自動的にキーワード引数(**kwargs
)に変換され、第2引数の**{}
が無視されていることによるものとのことでした。
たしかに、このコードを見た時「bar
の第1引数に{}
に渡しているから、出力結果としては[{},{}]
かな」と思いました。
記事の説明にあるとおり、混乱してしまいました。
今後、キーワード引数をどう使っていくか
キーワード引数と通常の引数が完全に分離されたことにより、キーワード引数を受け取りたいメソッドの定義は、いずれかの形にすべきとされています。
def foo(k: default)
def foo(k:)
def foo(**kwargs)
一方で、Ruby3.0でもキーワード引数を受け取らないメソッドを呼び出す時にキーワード引数を渡すことは、これまで同様にできるとのことです。
def foo(kwargs = {}) kwargs end foo(k: 1) #=> {:k=>1}
こちらについては、禁止した場合のメリットが少ないことから、引き続き動作するようですが、新たなコードを書くときにはおすすめできないとされていました。
単に今までと同じように動くからよいではなく、今渡した引数がハッシュなのか、キーワード引数なのかをきちんと考えた上でコードを読み書きすることで、既存のコードもより良いコードにしていくことができそうです。
さいごに
今回、変更点の背景について調べることで、非推奨や禁止にはなっていないものの、新たにコードを書く時に使用しない方がよい書き方を知ることができました。
これはただ単に変更点とその対応方法について、調べるだけではわからなかったことです。
今回、学んだことは新たにコードを書く時や既存のコードを修正する時に活かしていきます!