Ruby の Warning[:experimental] = false は実行時に反映されないケースがある

某所でそういう話が出ていたのでいろいろと調べてまとめてみました。

Warning[:deprecated] = true / false で実行時に非推奨な警告を制御できる

Warning[:deprecated]true / false を割り当てることで実行時に非推奨な警告の出力を制御することができます。
例えば Dir.exists?Ruby 3.0 以降では非推奨なので Warning[:deprecated] が真だと警告文が出力されます。
ちなみに Ruby 2.7 系で試す場合は Enumerator.new([], :each) など使うと警告が出ます。

# Ruby 2.7.2 以降だとデフォルトでは false
pp Warning[:deprecated]
# => false

# no wanirng
Dir.exists?("")

# これに true を設定するとそれ以降に呼ばれたメソッドなどで警告が出るようになる
Warning[:deprecated] = true

# warning: Dir.exists? is deprecated; use Dir.exist? instead
Dir.exists?("")

こんな感じで実行時に警告文の出力を制御することができます。

Warning[:experimental] = true / false の場合は?

Ruby 3.0 ではいくつかの機能が実験的に導入されています。
例えば RactorRuby 3.0 時点ではまだ実験的な機能になります。
さて、このような実験的な機能を使用すると警告文が出力されることがあります。

# warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
Ractor.new {}

この警告文の出力ですが Warning[:experimental] を用いることで『実験的な機能に対する』警告文の出力を制御することができます。

# デフォルトでは true
pp Warning[:experimental]
# => true

# 警告が出る
# warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
Ractor.new {}

# 警告の出力を無効にする
Warning[:experimental] = false

# 警告が出ない
# no warning
Ractor.new {}

このように Warning[:experimental] = false にする事で警告文が出力されなくなります。

Warning[:experimental] = false で制御できないケースもある

Ruby 3.0 ではパターンマッチの in を 1行でかける構文も実装されました。
これも Ractor 同様にまだ実験的な機能なので使用すると警告文が出ます。

user = { name: "homu", age: 14 }

# warning: One-line pattern matching is experimental, and the behavior may change in future versions of Ruby!
if user in { age: 1..20 }
  # ...
end

ではこの警告文を Warning[:experimental] で制御することができるのでしょうか?結論から言うとできませんでした。

user = { name: "homu", age: 14 }

# 実験的な警告を無効にする
Warning[:experimental] = false

# 実験的な警告を無効にしているのに警告が出てしまう
# warning: One-line pattern matching is experimental, and the behavior may change in future versions of Ruby!
if user in { age: 1..20 }
  # ...
end

これはなぜかというと in を使ったときの警告文の出力が『コンパイル時』に制御されているためです。
なので次のように Ractor.new が実行時に呼ばれないと警告文を出さないのに対して、 in の場合は実行時に呼ばれていなくても警告文がでます。

def foo
  # これは実行時に foo メソッドが呼ばれていないので警告はでない
  Ractor.new
end

def hoge
  # これは実行時に hoge メソッドが呼ばれていなくても警告が出る
  42 in n
end

一方で Warning[:experimental] = false というコードは『実行時』に制御を行っているため、コンパイル時には反映されません。
なので例えば eval などを使って実行時に Ruby のコードを実行する場合は Warning[:experimental] の設定が反映されます。

user = { name: "homu", age: 14 }

Warning[:experimental] = true

# warning: One-line pattern matching is experimental, and the behavior may change in future versions of Ruby!
eval <<~EOS
if user in { age: 1..20 }
  # ...
end
EOS

Warning[:experimental] = false

# no warning
eval <<~EOS
if user in { age: 1..20 }
  # ...
end
EOS

このように Warning[:experimental] で警告文の出力を制御する場合は反映される機能とそうでない機能があるので注意する必要があります。

実験的な機能の警告を無効にするには?

Ruby のコマンドオプションに -W:no-experimental を付けることで警告文を無効にすることができます。

$ cat main.rb
user = { name: "homu", age: 14 }

if user in { age: 1..20 }
  # ...
end
# 警告が出る
$ ruby main.rb
main.rb:3: warning: One-line pattern matching is experimental, and the behavior may change in future versions of Ruby
# -W:no-experimental を付けると警告がでない
$ ruby -W:no-experimental main.rb
$

実験的な機能の警告文を無効にしたい場合はコマンドオプションで制御する方がよさそうですね。