【一人 bugs.ruby Advent Calendar 2020】[Feature #17371] Reintroduce `expr in pat`【17日目】

一人 bugs.ruby Advent Calendar 2020 17日目の記事になります。

[Feature #17371] Reintroduce expr in pat

これは1行 in を再度入れ直そう、という旨のチケットです。
これ、ちょっとコンテキストが複雑なんですが、元々 Ruby 2.7 で 1行 in という機能が入りました。
1行 in は元々真理値を返す挙動で以下のように条件分岐で利用する事ができました。

# name が String 型で age が20以下の場合にマッチする
if user in { name: String, age: (..20) }
  puts "OK"
end

しかし、その後 [Feature #16355] Raise NoMatchingPatternError when expr in pat doesn't match で『条件にマッチしなかった場合でも変数が参照できるのは聞けなのではないか』という話がでて、最終的にマッチしなかった場合は例外 NoMethodError を発生させるようにしました。

# こんなコードを書いてマッチしなかった場合は x が nil になる
expr in [0, x]
# ...
# (snip)
# ...
# ここで nil を参照してしまうのは危険ではないだろうか?
x.foo

これが Ruby 2.7 時点での挙動になります。
その後 Ruby 3.0 の開発中にこの 1行 in=> へと置き換わりました。
このあたりの話は以下の記事を参照してください。

なので 1行 inRuby 3.0 では(一旦)廃止される予定でした。
しかし、『値を割り当てるのは => で行うことで回避できる』という理由で再び『真理値を返す in 』が追加されました。
これを利用すると以下のように条件分岐でパターンマッチを使うことができます。

# name が String 型で age が20以下の場合にマッチする
if user in { name: String, age: (..20) }
  puts "OK"
end
users = [
  { name: "homu", age: 14 },
  { name: "mami", age: 15 },
  { name: "mado", age: 14 },
]
# こんな感じで絞り込むことができる
pp users.select { _1 in { name: /^m/, age: (...15) } }

これは普通に欲しい機能だったので Ruby 3.0 にはいって嬉しいです。
ただし、この機能を使用すると experimental warning が出るので注意してください。

# warning: One-line pattern matching is experimental, and the behavior may change in future versions of Ruby!
if user in { name: String, age: (..20) }
  puts "OK"
end