はじめに
Ruby 3.4 でブロックパラメーター it
が導入された。
一言で言えば
(1..3).map{ _1.to_s }
が
(1..3).map{ it.to_s }
と書ける,というもの。
既にさまざまなところで紹介されているので,本記事ではその「さまざまなところ」にはなぜかあまり書かれていない落とし穴についてのみ述べる。
落とし穴
it
の何が怖いかは,以下のコードで一目瞭然:
ary = [1, 2, 3]
p ary.map{ it.to_s }
# => ["1", "2", "3"]
# 期待どおり
it = 110
p ary.map{ it.to_s }
# => ["110", "110", "110"]
# えっ?
二番目の ary.map
のブロックでは,it
がブロックパラメーターではなく,ブロック外で定義されたローカル変数の参照となってしまっている。
この行を見ただけでは,it
がブロックパラメーターなのかローカル変数なのか区別がつかないのだ。
見つけにくいバグを生みそう。
ブロック内の it
がこのどちらであるのかは,スクリプトを上のほうまで遡らないと分からない。
it
導入の動機の一つは認知負荷の低減にあったというが,これでは逆効果ではないのだろうか。
対策
つまらないバグを防止する対策としては以下の二つが思いつく。
-
it
というローカル変数は使わない - ローカル変数のスコープが短くなるよう心がける
前者はコーディングルールとして明記したほうがいいかもしれない。
「it
なんてローカル変数,ふつう使わないでしょ」と思うかもしれないが,アゼルバイジャン語など多くのトルコ系言語で「it」は「犬」という単語なので(知らんけど),犬を表す変数に使わないとも限らない。
あるいは
ja = "ルビー"
en = "ruby"
de = "Rubin"
fr = "rubis"
it = "rubino"
のように,イタリア語の言語コード「it」を変数に使うこともありうる。
後者は,たとえばメソッド定義内にあるブロックで it
が使われている場合,メソッドがせいぜい 10 行とかなら,どっちの it
なのか一目で分かるでしょう,ということ。
いずれにしても,これらの「対策」は自分(やチーム)がコードを書くときの話であって,他人の書いたコードがこうなっていることは期待できない。
メソッド呼び出しと紛れるおそれは?
メソッド it
を定義しても,ブロック内の it
がメソッド呼び出しと解釈されることはないようだ。
def it
Math::PI
end
p (1..3).map{ it.to_s }
# => ["1", "2", "3"]
(もちろん,it
に ( )
が付いていたり,引数と解釈しうる式が後続していたりすればメソッド呼び出しとなる)
最後に
Let it
go.