【Ruby 3.4 Advent Calender】ブロック引数を利用しないメソッドにブロック引数を渡すと警告がでる【6日目】
Ruby 3.4 Advent Calender 6日目の記事です。
これはなに
今年 2024年12月25日にリリースされる予定の Ruby 3.4 の新機能や変更点などを1つずつ紹介していく Advent Calender になります。
基本的には NEWS に載っている機能を紹介すると思うんですがここにない機能についても書くかもしれません。
また、記事を書いてる時点ではまだ Ruby 3.4 はリリースされる前なので Ruby 3.4 がリリースされた時点で機能が変わっている 可能性があるかもしれないので注意してください。
記事のまとめは ここを参照 してください。
ブロック引数を利用しないメソッドにブロック引数を渡すと警告がでる
Ruby 3.4 から次のように『ブロック引数を受け取らない(利用していない)メソッドに対してブロック引数を渡す』と警告が出るようになりました。
# このメソッドではブロック引数は利用していない
def hoge
end
# メソッド内でブロック引数を利用していない場合に警告がでる
# warning: the block passed to 'Object#hoge' defined at test.rb:2 may be ignored
hoge {}
この警告は**-W
オプションがあるときのみ**出力されます。
# test.rb
def hoge
puts "hello"
end
hoge {}
# -w がない場合は警告はでない
$ ruby test.rb
hello
# -W がある場合に警告が出る
$ ruby -W test.rb
test.rb:6: warning: the block passed to 'Object#hoge' defined at test.rb:2 may be ignored
hello
また類似で -W:strict_unused_block
オプションもあるんですがこれは後述で説明します。
背景
これは『ブロック引数を渡してもそれが利用されていないこと』を明示的に検知したいのが目的となっています。
例えば次のように『意図しないブロック引数の渡し方』をしてしまった場合にユーザが気づくのが難しいので、それに気づけるようにしたい感じですね。
def my_open(name)
open(name)
end
# my_open 自体はブロック引数を受け取らないが、
# Kernel#open と同じような感じでブロック引数と渡してしまうと意図する挙動にならない
my_open(name){ |f| important_work_with f }
もう少し細かい話
以下のケースに該当する場合は『ブロック引数が利用されている』という風に定義されます。
- (1) メソッドの引数にブロック引数がある場合(
hoge(&block)
) - (2) メソッド内で
yield
を利用している場合 - (3) メソッド内で
super
を利用したときにブロック引数をフォワードしている場合
なので次のようなメソッドに対してブロック引数を渡した場合は警告はでません。
# (1) メソッドの引数にブロック引数がある場合( `hoge(&block)` )
def with_block(&block)
end
# no warning
with_block {}
# (2) メソッド内で `yield` を利用している場合
def use_yield
yield
end
# no warning
use_yield {}
『(3) メソッド内で super
を利用したときにブロック引数をフォワードしている場合』がちょっとややこしくて super
では暗黙的に『自身のブロック引数が親メソッドに渡される』ようになっています。
この『自身のブロック引数が親メソッドに渡される = ブロック引数が利用されている』という解釈になります。
逆に super {}
のように呼び出した場合は『暗黙的にブロック引数が渡されない = ブロック引数が利用されていない』という解釈になります。
# (3) メソッド内で `super` を利用したときにブロック引数をフォワードしている場合
class Super
def case1; end
def case2; end
end
class Sub < Super
# super だけの場合
# このときは super に暗黙的に自身のブロック引数を渡している
def case1
super
end
# super にブロック引数を渡している場合
# このときは super に自身のブロック引数は渡されない = ブロック引数は利用されていない
def case2
super {}
end
end
sub = Sub.new
# no warning
sub.case1 { pp "case1" } # => nil
# warning: the block passed to 'Sub#case2' defined at test.rb:16 may be ignored
sub.case2 { pp "case2" } # => nil
また『定義されているクラスは違うが、いずれかの同名のメソッドでブロック引数を利用している場合』は警告はでないようになっています。
class X
def hoge
yield
end
end
class Y
def hoge
end
end
# Y#hoge だけを呼び出した場合でも警告がでない
# no warning
Y.new.hoge {}
これは -W:strict_unused_block
オプションや Warning[:strict_unused_block]
で警告を出すように制御することができます。
class X
def hoge
yield
end
end
class Y
def hoge
end
end
Warning[:strict_unused_block] = true
# warning: the block passed to 'Y#hoge' defined at test.rb:8 may be ignored
Y.new.hoge {}
その他、詳しい挙動に関しては下記を参照してください。
Discussion