📚

【Ruby 3.4 Advent Calender】_1 の代替として it が利用できる【1日目】

2024/12/01に公開

Ruby 3.4 Advent Calender 1日目の記事です。

これはなに

今年 2024年12月25日にリリースされる予定の Ruby 3.4 の新機能や変更点などを1つずつ紹介していく Advent Calender になります。
基本的には NEWS に載っている機能を紹介すると思うんですがここにない機能についても書くかもしれません。
また、記事を書いてる時点ではまだ Ruby 3.4 はリリースされる前なので Ruby 3.4 がリリースされた時点で機能が変わっている 可能性があるかもしれないので注意してください。
記事のまとめは ここを参照 してください。

_1 の代替として it が利用できるようになる

ブロック内で仮引数を定義しなかった場合に『ブロックの第一引数を _1 で参照すること』ができます。

# .map { |_1| _1 + _1 } みたいな感じでブロックに渡された第一引数を `_1` で参照することができる
pp [1, 2, 3].map { _1 + _1 }

Ruby 3.4 ではこの _1 の代替として it が利用できるようになりました。

pp [1, 2, 3].map { it + it }

わたしは以下のような問題と it があんまり特別なキーワードに見れないのであんまり好きではないんですが it のほうがいいっていう人も結構いるみたいですね。

注意点

_1 の場合は同名のメソッドや変数を定義することは禁止されています。

# error: _1 is reserved for numbered parameters
def _1
  42
end
# error: _1 is reserved for numbered parameters
_1 = 42

しかし it の場合は依然として同名のメソッドや変数を定義することは許容されています。

def it
  42
end

pp it # => 42
it = 42
pp it # => 42

なので例えば次のように『 it という名前のメソッドや変数が定義されている場合にブロック内で it を参照したらどうなるのか』という問題があります。

it = 42

# ブロック内の it は何を参照する???
[1, 2, 3].map { it + it }

これなんですが同名のメソッドが定義されているとき変数が定義されているときで挙動が変わります。

it メソッドが定義されている場合

it メソッドが定義されているときは『メソッド呼び出しっぽいとき』にメソッドの it が呼び出され、そうないときはブロックの引数を参照します。
なので以下のような it の参照はすべて『メソッドの it が呼び出される』ことになります。

def it(*) = "#it"

# 引数があるのでメソッド呼び出し
pp 42.then { it 42 } # => "#it"

# () があるのでメソッド呼び出し
pp 42.then { it() } # => "#it"

# ブロック引数があるのでメソッド呼び出し
pp 42.then { it {} } # => "#it"

# self. があるのでメソッド呼び出し
pp 42.then { self.it } # => "#it"

# send などで呼び出された場合はメソッド呼び出し
pp 42.then { send(:it) } # => "#it"

また、例外的な話で以下のように eval() 内で it を参照したときもメソッドの it を参照します。

pp 42.then { eval("it") } # => "#it"

it 変数が定義されている場合

it 変数が定義されている場合は『変数のほうが優先して』参照されます。

it = "it"
pp 42.then { it } # => "it"

また明示的にブロックの仮引数を定義すればその値を参照することができます。

it = "it"
pp 42.then { |it| it } # => 42

このあたりが _1 と比べて少し難読なので注意する必要があります。

関連

GitHubで編集を提案

Discussion