🕌
[Feature #20742] 後置if で代入式を定義したときに警告を出すチケット
[Feature #20742] Trying to assign to a variable in statement modifier should emit a warning
- 次のように後置if の条件式で代入した変数を左辺で参照すると実行時エラーになることがあります
p a if a = 0.zero? # raises NameError “undefined local variable or method ‘a’”.
- これは事前に変数
a
が定義されている場合ではエラーになりません
a = nil
p a if a = 0.zero? # no error
- このような挙動の違いがあるので後置if で代入式を書いた場合は警告を出すようにするチケットです、多分
- これなんですがそもそもなんでエラーになるのかって話をすると Ruby のパーサーの評価順の問題になります
p a if a = 0.zero?
- 上記のコードではまず
p a
がパースされます - このときにパーサでは
a
は『メソッド』として認識されます - 次に
if a = 0.zero?
が評価されて『a
の変数が定義されている』と認識されます - その上で実際にコードが実行されると
p a
を評価するときに『a
はメソッドなのでメソッドを呼び出そうとする。しかし、a
メソッドはないのでNameError
になる』という挙動になります - なので
a
メソッドが定義されている場合は問題なく動作します
def a = 42
# p a は変数ではなくてメソッドの a を呼び出す
p a if a = 0.zero?
# => 42
- 逆にいうと後置if でない場合は『先に if 式がパースされる』ので
a
変数を参照します
def a = 42
# 先に a = 0.zero? がパースされて『変数 a が存在している』という前提で後続のコードがパースされる
if a = 0.zero?
# なのでここの a はメソッドではなくて変数を参照する
p a
end
- この話は https://bugs.ruby-lang.org/issues/20742#note-6 に詳しくかいてあります
- で、元々のチケットの話なんですがこういうコードは標準ライブラリで使われているなど一般的なケースとコメントされていますね
- パッと見、代入した変数を左辺で参照しているというよりかは後続で参照しているみたいなことが多いんですかね?
# lib/net/http.rb の一部のコードを抜粋
# ここで代入した opt は後続で参照している
arg.pop if opt = Hash.try_convert(arg[-1])
# ...
if opt
if opt[:use_ssl]
opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt)
end
end
Discussion