Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Ruby 3: 引数をforwardする`…`記法が第2パラメータでも使えるようになった(翻訳)

概要

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

日本語タイトルは内容に即したものにしました。

読みやすさのため、原文の「argument」は訳文でメソッド定義の場合は「パラメータ」、メソッド呼び出しの場合は「引数」と表記を分けました。

Ruby 3: 引数をforwardする...記法が第2パラメータでも使えるようになった(翻訳)

Ruby 2.7で、引数をメソッドにforwardする...というショートハンド機能が追加されました。引数のforwardについて手軽におさらいしたい方は、私たちの昨年の記事「Ruby 2.7 adds shorthand syntax for arguments forwarding」をご覧ください。

変更前

...記法は、以下のようにパラメータ全体をメソッドにforwardする書き方に限定されていました。

def travel(...)
  by_road(...)
end

しかし、以下のように第1パラメータを別にして、第2パラメータ以降を...でforwardしたい場合もあります。

def travel(preference, ...)
  if preference == "air"
    by_flight(...)
  else
    by_road(...)
  end
end

変更後

Ruby 3.0で、第1パラメータに加えて、第2パラメータ以降を...でforwardできるようになりました(#3190)。

以下のコード例で理解してみましょう。

def transform(a, ...)
  process(a, ...)
end

def process(a, *args, **kwargs, &block)
  if block
    block.call(args, kwargs)
  else
    [a, args, kwargs]
  end
end

このコード例では、transformメソッドにaという位置パラメータと...パラメータが宣言されています。

このメソッド内で呼び出しているprocessメソッドには、transformメソッドと同じパラメータが引数として渡されています。そしてprocessメソッド定義のシグネチャには、aという位置パラメータの他に、*args, **kwargs, &blockも含まれています。

ここでのポイントは、...という記法が以下のように振る舞うことです。

  • 追加引数(複数可)が*argsに代入される
  • キーワード引数(複数可)が**kwargsに代入される
  • ブロックが&blockに代入される

transformメソッドにいくつか値を渡して、上のコードで実験してみましょう。

コード例1: 「位置引数」のみを渡す場合

  transform(1) # => [1, [], {}]

この例では、transformメソッドのaパラメータに1という値が引数として渡され、これがprocessメソッドに渡されます。

processメソッドのblockパラメータにはブロックが渡されていないので、processメソッドのelse部が実行され、[a, args, kwargs]a1という値が代入されます。

同様に、processメソッドのargsパラメータには追加の引数が渡されておらず、kwargsパラメータにもキーワード引数が渡されていません。

そしてargsのデフォルト値は[]kwargsのデフォルト値は{}です。

したがって、出力は[1, [], {}]になります。

コード例2: 「位置引数」「追加引数」を渡す場合

  transform(1, 2, 3) # => [1, [2, 3], {}]

この例のaは位置パラメータなので、位置パラメータaには1が代入され、...には2, 3が代入されます。

2, 3は追加引数と認識されるので、processメソッドの追加パラメータargsに代入されます。

したがって、出力は[1, [2,3], {}]になります。

コード例3: 「位置引数」「追加引数」「キーワード引数」を渡す場合

transform(1, 2, 3, a:1, b: 2) # => [1, [2, 3], {:a=>1, :b=>2}]

この場合、パラメータaには位置引数1が代入され、argsパラメータには追加引数[2, 3]が代入され、kwargsパラメータにはキーワード引数{:a=>1, :b=>2}が代入されます。

したがって、出力は[1, [2, 3], {:a=>1, :b=>2}]となります。

コード例4: ブロックを渡す場合

transform(1, 2, 3, a:1, b: 2) { |args, kwargs|  [args, kwargs]  }
 # =>  [[2, 3], {:a=>1, :b=>2}]

この場合、ブロックパラメータblockにブロックが渡されるので、processメソッドのif部分が実行されることになります。

processメソッドのif部分では、受け取ったブロックに追加引数とキーワード引数を渡してブロックを実行し、そのblockは渡された引数を配列にして返します。

したがって、出力は[[2, 3], {:a=>1, :b=>2}]になります。

関連記事

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


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。