サンプルコードでわかる！Ruby 3.1の主な新機能と変更点

## はじめに

🎄🎄🎄この記事は[Ruby Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ruby) 25日目の記事です🎄🎄🎄

Rubyは毎年12月25日にアップデートされます。
Ruby 3.1については2021年12月25日に3.1.0が正式リリースされました。

https://www.ruby-lang.org/ja/news/2021/12/25/ruby-3-1-0-released/

この記事ではRuby 3.1で導入される変更点や新機能について、サンプルコード付きでできるだけわかりやすく紹介していきます。

ただし、しっかり内容を確認する時間が取れなかった機能に関してはNEWS.mdの記載内容を転記するだけに留めています :pray: 

### 本記事の情報源
本記事は以下のような情報源をベースにして、記事を執筆しています。

- [NEWS.md](https://github.com/ruby/ruby/blob/v3_1_0/NEWS.md)
- NEWS.mdに記載されている各種issueとそれに関連するdiff
- [Ruby 3.1.0リリース](https://www.ruby-lang.org/ja/news/2021/12/25/ruby-3-1-0-released/)
- [プロと読み解く Ruby 3.1 NEWS - クックパッド開発者ブログ](https://techlife.cookpad.com/entry/2021/12/25/220002)
- @pink_bangbi さんの[ブログ](https://secret-garden.hatenablog.com/)や[スライド](https://www.docswell.com/s/pink_bangbi/KXWG2Z-2021-12-17-185213)
- その他、ネット上の情報源

### 動作確認したRubyのバージョン

本記事は以下の環境で実行した結果を記載しています。

```
$ ruby -v
ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [arm64-darwin21]
```

### フィードバックお待ちしています
本文の説明内容に間違いや不十分な点があった場合はコメント欄から指摘 or 修正をお願いします🙏

それでは以下が本編です！

## 言語仕様の変更

### 他のメソッドにブロックを渡すだけならメソッド定義時に引数名を付けなくてもよくなった

Ruby 3.1では他のメソッドにブロックを渡すだけならメソッド定義時に引数名を付けなくてもよくなりました（匿名ブロック）。

```ruby
def foo(&)
  bar(&)
end

def bar(&b)
  b.call
end

foo { puts 'hi' }
#=> hi
```

Ruby 3.0だと上のコードは構文エラーになります。

```
lib/sample.rb:1: syntax error, unexpected ')', expecting local variable or method
def foo(&)
lib/sample.rb:2: syntax error, unexpected ')'
  bar(&)
```

https://bugs.ruby-lang.org/issues/11256

### パターンマッチのピン演算子にローカル変数以外の式も渡せるようになった

Ruby 3.1ではパターンマッチのピン演算子にローカル変数以外の式（メソッド呼び出し等）も渡せるようになりました。以下はピン演算子に`find_name`というメソッド呼び出しを渡す例です。

```ruby
def find_name = 'Alice'

name = 'Alice'
case name
in ^(find_name)
  puts 'found'
end
#=> found
```

Ruby 3.0だと上のコードは構文エラーになります。

```
lib/sample.rb:5: syntax error, unexpected (, expecting local variable or method
in ^(find_name)
lib/sample.rb:7: syntax error, unexpected `end', expecting end-of-input
```

式を渡す場合は`()`で囲む必要があります。`()`で囲まない場合はローカル変数であることが期待されるため、エラーになります。

```
# in ^find_name のように()を省略した場合
lib/sample.rb:5: find_name: no such local variable
```

https://bugs.ruby-lang.org/issues/17411

### パターンマッチのピン演算子にインスタンス変数、クラス変数、グローバル変数が渡せるようになった

Ruby 3.0までピン演算子に渡せる変数はローカル変数だけでしたが、Ruby 3.1ではインスタンス変数（`@foo`など）やクラス変数（`@@foo`など）、グローバル変数（`$foo`など）も渡せるようになりました。

```ruby
@foo = 'Alice'

name = 'Alice'
case name
in ^@foo
  puts 'found'
end
```

Ruby 3.0だと上のコードは構文エラーになります。

```
lib/sample.rb:5: syntax error, unexpected instance variable, expecting local variable or method
in ^@foo
lib/sample.rb:7: syntax error, unexpected `end', expecting end-of-input
```

https://bugs.ruby-lang.org/issues/17724

### 1行パターンマッチが実験的機能でなくなった

Ruby 3.0で実験的機能として導入された1行パターンマッチが実験的機能でなくなりました。Ruby 3.1では実行しても警告が出ません。

```ruby
if [1, 2, 3] in [Integer, Integer, Integer]
  puts 'matched'
end
#=> matched

{name: 'Alice'} => {name:}
puts name  #=> Alice
```

Ruby 3.0だと上のコードは実行可能ですが警告が出ます。

```
lib/sample.rb:1: warning: One-line pattern matching is experimental, and the behavior may change in future versions of Ruby!
lib/sample.rb:5: warning: One-line pattern matching is experimental, and the behavior may change in future versions of Ruby!
matched
Alice
```

ちなみに、findパターンはRuby 3.1でもまだ実験的機能のままになっているようです。

```ruby
# findパターンを使うとRuby 3.1でも警告が出る
case ['Bob', 'Alice', 'Carol']
in [*others_before, 'Alice', *others_after]
  # ...
end
#=> warning: Find pattern is experimental, and the behavior may change in future versions of Ruby!
```

### 1行パターンマッチで右辺の括弧が省略できるようになった

Ruby 3.1では1行パターンマッチで右辺の括弧が省略できるようになりました。

```ruby 
[0, 1] => _, x
{y: 2} => y:
p x #=> 1
p y #=> 2
```

Ruby 3.0では下のように括弧を付けないと構文エラーが起きていました。

```ruby 
# Ruby 3.0
[0, 1] => [_, x]
{y: 2} => {y:}
p x #=> 1
p y #=> 2
```

https://bugs.ruby-lang.org/issues/16182

### 多重代入の評価順序が単一代入の評価順序と同じになった

（このセクションはNEWS.mdの内容を意訳します）

Ruby 3.1では多重代入の評価順序が単一代入の評価順序と同じになりました。

単一代入の場合、Rubyは左から右の順に評価します。たとえば下のコードであれば、

```ruby
foo[0] = bar
```

次のような順番で評価されます。

1. `foo`
2. `bar`
3. `foo` の結果に対して `[]=` が呼ばれる

Ruby 3.0以前のRubyでは多重代入はこの順序に従っていませんでした。たとえば以下のコードであれば、

```ruby
foo[0], bar.baz = a, b
```

Ruby 3.0以前のRubyでは次のような順序で評価されていました。

1. `a`
2. `b`
3. `foo`
4. `foo` の結果に対して `[]=` が呼ばれる
5. `bar`
6. `bar` の結果に対して `baz=` が呼ばれる

Ruby 3.1からはこの順序が単一代入と同様に、左辺が先、右辺が後に評価されるようになります。

1. `foo`
2. `bar`
3. `a`
4. `b`
5. `foo` の結果に対して `[]=` が呼ばれる
6. `bar` の結果に対して `baz=` が呼ばれる

https://bugs.ruby-lang.org/issues/4443

### ハッシュリテラルとキーワード引数で値を省略できるようになった

Ruby 3.1ではハッシュリテラルとキーワード引数で値を省略できるようになりました。

```ruby
name = 'Alice'
age = 20

# 以下のハッシュリテラルは {name: name, age: age} と書いたのと同じ
h = {name:, age:}
puts h #=> {:name=>"Alice", :age=>20}
```

```ruby
text = "ABC\nDEF"
chomp = true

# text.lines(chomp: chomp) と書いたのと同じ
p text.lines(chomp:) #=> ["ABC", "DEF"]
```

この記法でキー名として使用できるのは定数名、ローカル変数名、メソッド名です。

```ruby
# メソッド名をキー名として使用する場合
def name = 'Alice'
def age = 20

h = {name:, age:}
puts h #=> {:name=>"Alice", :age=>20}

# 定数名をキー名として使用する場合
FOO = 10
BAR = 20

h = {FOO:, BAR:}
puts h #=> {:FOO=>10, :BAR=>20}
```

https://bugs.ruby-lang.org/issues/14579

この変更点についてより詳しく知りたい場合は以下の記事をご覧ください。

https://nacl-ltd.github.io/2021/12/20/hash-value-omission.html

### Ractor

> Non main-Ractors can get instance variables (ivars) of classes/modules if ivars refer to shareable objects.
> （筆者訳）非メインRactorはクラスやモジュールのインスタンス変数が共有可能オブジェクトを参照していれば、そのインスタンス変数を取得できるようになった。

とのことですが、サンプルコードは用意できなかったので、以下のIssueを参照してください。

https://bugs.ruby-lang.org/issues/17592

### エンドレスメソッド定義でコマンド構文が書けるようになった

Ruby 3.1ではエンドレスメソッド定義で`puts 'Hello'`のようなコマンド構文（引数を丸かっこで囲まないメソッド呼び出し）が書けるようになりました。

```ruby
def greet = puts 'Hello'

greet #=> Hello
```

Ruby 3.0だと上のコードは構文エラーになります。

```
lib/sample.rb:1: syntax error, unexpected string literal, expecting `do' or '{' or '('
def greet = puts 'Hello'
```

なお、Ruby 3.0の場合、`puts('Hello')`のように丸かっこを使えば構文として有効です。

```ruby
# Ruby 3.0の場合は丸かっこが必要
def greet = puts('Hello')

greet #=> Hello
```

ただし、Ruby 3.1でも`private def greet`のように書く場合は丸かっこがないと構文エラーになります。

```ruby
# Ruby 3.1でも構文エラー
private def greet = puts 'Hello'
#=> lib/sample.rb:1: syntax error, unexpected string literal, expecting `do' or '{' or '('
#   private def greet = puts 'Hi'

# 丸かっこを使えば有効（Ruby 3.0でも有効）
private def greet = puts('Hello')
```

https://bugs.ruby-lang.org/issues/17398

## コマンドラインオプション

### `--disable-gems` オプションがデバッグ目的でのみ使うように明言された

Ruby 3.1からrubygemsを無効化する `--disable-gems` オプションがデバッグ目的でのみ使用するように明言されました。オプションの指定時の挙動は同じですが、Ruby 3.1では`ruby --help`を開くと"only for debugging"と書かれています。

```
# Ruby 3.0
Features:
  gems            rubygems (default: enabled)

# Ruby 3.1
Features:
  gems            rubygems (only for debugging, default: enabled)
```

https://bugs.ruby-lang.org/issues/17684

## コアクラス（組み込みライブラリ）のアップデート

## Array

### 共通要素が1件以上あればtrueを返すintersect?メソッドが追加された

Ruby 3.1では二つの配列の積集合の結果（つまり共通要素）が1件以上あればtrueを返す`intersect?`メソッドが追加されました。

```ruby
a = [1, 2, 3]
b = [3, 4, 5]

# 3が共通要素なのでtrue
puts a.intersect?(b) #=> true

c = [4, 5, 6]

# 共通要素がないのでfalse
puts a.intersect?(c) #=> false
```

ちなみにRuby 3.0以前では次のように書く必要がありました。

```ruby
puts (a & b).any? #=> true
puts (a & c).any? #=> false
```

https://bugs.ruby-lang.org/issues/15198

（参考情報）SetクラスにはRuby 3.0以前から`intersect?`メソッドが存在しています。

```ruby
require 'set'

a = Set[1, 2, 3]
b = Set[3, 4, 5]

puts a.intersect?(b) #=> true

c = Set[4, 5, 6]

puts a.intersect?(c) #=> false
```

## Class

### 
継承関係上の子孫クラスを返すdescendantsメソッドが追加された


`descendants`メソッドの導入はRuby 3.1では見送られました（[参考](https://bugs.ruby-lang.org/issues/14394#note-33)）。


Ruby 3.1では継承関係上の子孫クラスを返す`descendants`メソッドが追加されました。このメソッドはレシーバクラスを直接、または間接的に継承しているクラスを配列で返します。ただしレシーバ自身や特異クラスは戻り値に含みません。


```ruby
# 注：Ruby 3.1ではdescendantsメソッドの導入は見送られました
class Animal; end
class Mammal < Animal; end
class Dog < Mammal; end
Animal.descendants #=> [Mammal, Dog]
Mammal.descendants #=> [Dog]
Dog.descendants    #=> []
```

### 継承関係上のサブクラスを返すsubclassesメソッドが追加された

Ruby 3.1では継承関係上のサブクラスを返す`subclasses`メソッドが追加されました。このメソッドはレシーバクラスを直接継承しているクラスを配列で返します。ただしレシーバ自身や特異クラスは戻り値に含みません。

```ruby
class Animal; end
class Mammal < Animal; end
class Dog < Mammal; end
class Cat < Mammal; end
Animal.subclasses #=> [Mammal]
Mammal.subclasses #=> [Cat, Dog]
Dog.subclasses    #=> []
Cat.subclasses    #=> []
```

https://bugs.ruby-lang.org/issues/18273

## Dir

### Dir.globにFile::FNM_DOTMATCHオプションを渡したときに`..`が返らなくなった

（これはNEWS.mdに載っていない、個人的に気づいた変更点です）

`Dir.glob("*", File::FNM_DOTMATCH)`や`Dir.glob("**", File::FNM_DOTMATCH)`というコードを書いた場合、Ruby 3.0までは`.`と`..`が戻り値に含まれていましたが、Ruby 3.1では`..`が含まれなくなりました。

```ruby 
# Ruby 3.0 ("**"でも同じ)
Dir.glob("*", File::FNM_DOTMATCH)
#=> [".", "..", ".DS_Store", "foo.txt"]

# Ruby 3.1 ("**"でも同じ)
Dir.glob("*", File::FNM_DOTMATCH)
#=> [".", ".DS_Store", "foo.txt"]
```

https://bugs.ruby-lang.org/issues/17280

## Enumerable, Enumerable::Lazy

### compactメソッドが追加された

EnumerableクラスとEnumerable::Lazyクラスに`compact`メソッドが追加されました。これにより、配列やハッシュでなくてもEnumerableモジュールをincludeしているオブジェクトであれば`compact`メソッドが使えるようになります。

以下はEnumerableモジュールをincludeした自作クラスに対して`compact`メソッドを呼び出す例です。

```ruby
class Sample
  include Enumerable

  def each
    yield 3
    yield nil
    yield 7
    yield 9
    yield nil
  end
end

sample = Sample.new
p sample.compact #=> [3, 7, 9]
p sample.lazy.compact.force #=> [3, 7, 9]
```

https://bugs.ruby-lang.org/issues/17312

## Enumerable

### tallyメソッドが引数としてハッシュを受け取れるようになった

Ruby 3.1では`tally`メソッドが引数としてハッシュを受け取れるようになりました。

まず、`tally`メソッドについて簡単に説明しておきます。`tally`メソッドは同一の要素の個数を数え上げるメソッドです。

```ruby
requests = [:pizza, :ramen, :sushi, :ramen]
ret = requests.tally
puts ret #=> {:pizza=>1, :ramen=>2, :sushi=>1}
```

Ruby 3.1では引数としてハッシュを渡すことでそのハッシュに結果を格納することができます。

```ruby
requests = [:pizza, :ramen, :sushi, :ramen]
h = {}
ret = puts requests.tally(h)
# 戻り値だけでなく、引数のハッシュにも同じ結果が格納される
puts ret #=> {:pizza=>1, :ramen=>2, :sushi=>1}
puts h   #=> {:pizza=>1, :ramen=>2, :sushi=>1}
```

引数にハッシュを渡した場合、`tally`の戻り値は引数のハッシュと同一オブジェクトになります。

```ruby
ret.equal?(h) #=> true
```

引数のハッシュに同一キーが存在していれば、そこに`tally`の結果が加算されます。また、無関係なキーについては値はそのままになります。

```ruby
requests = [:pizza, :ramen, :sushi, :ramen]
h = {pizza: 10, sushi: 20, foo: :bar}
ret = requests.tally(h)
# pizzaとsushiにはカウントが加算される
# fooは無関係なので値は:barのまま
# ramenはキーと値が新規に追加される
puts ret #=> {:pizza=>11, :sushi=>21, :foo=>:bar, :ramen=>2}
puts h   #=> {:pizza=>11, :sushi=>21, :foo=>:bar, :ramen=>2}
```

同一のキーはあるが、加算できない値の場合は例外が発生します。

```ruby
h = {pizza: :none}
ret = requests.tally(h)
#=> lib/sample.rb:17:in `each': wrong argument type Symbol (expected Integer) (TypeError)
```

https://bugs.ruby-lang.org/issues/17744

### each_consとeach_sliceが戻り値としてレシーバを返すようになった

Ruby 3.1では`each_cons`メソッドと`each_slice`メソッドが戻り値としてレシーバを返すようになりました。

```ruby
ret = [1, 2, 3, 4].each_cons(2) { |arr| p arr }
#=> [1, 2]
#=> [2, 3]
#=> [3, 4]
p ret #=> [1, 2, 3, 4] (Ruby 3.0はnil)

ret = [1, 2, 3, 4].each_slice(2) { |arr| p arr }
#=> [1, 2]
#=> [3, 4]
p ret #=> [1, 2, 3, 4] (Ruby 3.0はnil)
```

ちなみにRuby 3.0以前ではどちらも`nil`が返ってきていました。

https://github.com/ruby/ruby/pull/1509

## File

### File.dirnameが第2引数でスキップするパスの階層数を指定できるようになった

Ruby 3.1では`File.dirname`メソッドが第2引数でスキップするパスの階層数を指定できるようになりました。

```ruby
path = "/a/b/c/d.txt"
puts File.dirname(path)    #=> /a/b/c
puts File.dirname(path, 0) #=> /a/b/c/d.txt
puts File.dirname(path, 1) #=> /a/b/c
puts File.dirname(path, 2) #=> /a/b
puts File.dirname(path, 3) #=> /a
puts File.dirname(path, 4) #=> /
puts File.dirname(path, 5) #=> /

# 負の値を指定した場合はエラー
puts File.dirname(path, -1)
#=> lib/sample.rb:9:in `dirname': negative level: -1 (ArgumentError)
```

https://bugs.ruby-lang.org/issues/12194

## GC

以下の2点がNEWS.mdに掲載されています。

> - "GC.measure_total_time = true" enables the measurement of GC. Measurement can introduce overhead. It is enabled by default. GC.measure_total_time returns the current setting. GC.stat[:time] or GC.stat(:time) returns measured time in milli-seconds.
> - GC.total_time returns measured time in nano-seconds.

詳しい内容については下記リンクを参照してください。

https://bugs.ruby-lang.org/issues/10917


## Integer

### to_int メソッドを呼びだして整数への変換を試みるInteger.try_convertが追加された

Ruby 3.1では`to_int`メソッドを呼びだして整数への変換を試みる`Integer.try_convert`が追加されました。

```ruby
# Integer.try_convertは引数に対してto_intメソッドを呼び出した結果を返す
p 1.to_int #=> 1
p Integer.try_convert(1) #=> 1
p 1.0.to_int #=> 1
p Integer.try_convert(1.0) #=> 1

# to_intメソッドを実装していない場合はnilが返る
p [].to_int
#=> lib/sample.rb:3:in `<main>': undefined method `to_int' for []:Array (NoMethodError)
p Integer.try_convert([]) #=> nil

# 配列にto_intメソッドを特異メソッドとして追加し、
# Integer.try_convertがto_intメソッドを呼び出していることを確認する
a = []
def a.to_int = 0
p Integer.try_convert(a) #=> 0
```

https://bugs.ruby-lang.org/issues/15211

## Kernel

### loadメソッドが第2引数としてモジュールを受け取れるようになった

Ruby 3.1では`load`メソッドが第2引数としてモジュールを受け取れるようになりました。この場合、指定されたファイルは第2引数のモジュールをトップレベルモジュールとしてloadされます。

```ruby 
# lib/target.rb
FOO = 123
def greet = 'Hello'
```

```ruby 
module Greetable; end

# lib/target.rbはGreetableモジュール内でloadされる（グローバルな名前空間を汚染しない）
load 'lib/target.rb', Greetable

# FOOはGreetable内の定数になる
puts Greetable::FOO #=> 123

# トップレベルにFOOは定義されていない
puts FOO #=> uninitialized constant FOO (NameError)

# BarクラスにGreetableモジュールをincludeする
class Bar
  include Greetable
end

# includeされたGreetable#greetを呼び出す（privateメソッドになっているのでsendを使う）
puts Bar.new.send(:greet) #=> Hello

# トップレベルにgreetは定義されていない
puts greet #=> undefined local variable or method `greet' for main:Object (NameError)
```

なお、Ruby 3.0以前でも第2引数に真の値を設定することでグローバル名前空間を汚染せずにloadする仕組みがありました。この仕組みはRuby 3.1でも引き続き有効です。

```ruby 
# target.rb
FOO = 123
puts "#{FOO} in target.rb"
``` 

```ruby 
# 第2引数に真を渡すとグローバル名前空間を汚染せずにloadできる
load 'lib/target.rb', true
#=> 123 in target.rb

# トップレベルにFOOは定義されていない
puts FOO #=> uninitialized constant FOO (NameError)
```

```ruby
# デフォルトの仕様でloadする
load 'lib/target.rb'
#=> 123 in target.rb

# FOOがトップレベルに定義される
puts FOO #=> 123
```

https://bugs.ruby-lang.org/issues/6210

## Marshal

### load時にオブジェクトをfreezeできるようになった

Ruby 3.1では`Marshal.load`メソッドに`freeze:`オプションが追加されました。このオプションを真にするとload時にオブジェクトをfreezeできます。

```ruby 
source = ["foo", {}, /foo/, 1..2]
payload = Marshal.dump(source)

# load時にオブジェクトをfreezeさせる
objects = Marshal.load(payload, freeze: true)

# 値はload前後でどちらも同じ
p source  #=> ["foo", {}, /foo/, 1..2]
p objects #=> ["foo", {}, /foo/, 1..2]

# 配列も配列の要素もすべてfreezeされる
p objects.frozen? 
#=> true
objects.each do |obj|
  puts "#{obj}=#{obj.frozen?}"
end
#=> foo=true
#=> {}=true
#=> (?-mix:foo)=true
#=> 1..2=true
```

ただし、クラスやモジュールはfreezeされません。

```ruby 
class Foo; end
module Bar; end

# クラスやモジュールをdump/loadする
source = [Foo, Bar]
payload = Marshal.dump(source)
objects = Marshal.load(payload, freeze: true)

p source  #=> [Foo, Bar]
p objects #=> [Foo, Bar]
p objects.frozen?
#=> true

# クラスとモジュールはfreeze: trueでloadしてもfreezeされない
objects.each do |obj|
  puts "#{obj}=#{obj.frozen?}"
end
#=> Foo=false
#=> Bar=false
``` 

https://bugs.ruby-lang.org/issues/18148

## MatchData

### キャプチャの結果を連番やキャプチャ名で取得できるmatchメソッドが追加された

Ruby 3.1ではキャプチャの結果を連番やキャプチャ名で取得できる`match`メソッドが追加されました。

```ruby
# mはMatchDataクラスのインスタンス
m = '2021-12-25'.match(/(\d+)-(\d+)-(\d+)/)
puts m.match(0) #=> 2021-12-25
puts m.match(1) #=> 2021
puts m.match(2) #=> 12
puts m.match(3) #=> 25
puts m.match(4) #=> index 4 out of matches (IndexError)

m = '2021-12-25'.match(/(?<year>\d+)-(?<month>\d+)-(?<day>\d+)/)
puts m.match(:year)  #=> 2021
puts m.match(:month) #=> 12
puts m.match(:day)   #=> 25
puts m.match(:foo)   #=> undefined group name reference: foo (IndexError)
```

このメソッドは`MatchData#[]`に似ていますが、`MatchData#[]`とは異なり配列を返すことはありません。

```ruby
# MatchData#[]もMatchData#matchと同じような使い方ができる
m = '2021-12-25'.match(/(?<year>\d+)-(?<month>\d+)-(?<day>\d+)/)
puts m[0]    #=> 2021-12-25
puts m[1]    #=> 2021
puts m[:day] #=> 25

# MatchData#[]は引数によっては配列を返すときがある
p m[1..3] #=> ["2016", "05", "08"]
p m[2, 2] #=> ["05", "08"]

# MatchData#matchは範囲や第2引数を与えることができない
m.match(1..3)
#=> no implicit conversion of Range into Integer (TypeError)
m.match(2, 2)
#=> wrong number of arguments (given 2, expected 1) (ArgumentError)
```

https://bugs.ruby-lang.org/issues/18172

### キャプチャの結果の文字列長を連番やキャプチャ名で取得できるmatch_lengthメソッドが追加された

Ruby 3.1ではキャプチャの結果の文字列長を連番やキャプチャ名で取得できる`match_length`メソッドが追加されました。

```ruby
m = '2021-12-25'.match(/(\d+)-(\d+)-(\d+)/)
puts m.match_length(0) #=> 10
puts m.match_length(1) #=> 4
puts m.match_length(2) #=> 2
puts m.match_length(3) #=> 2
puts m.match_length(4) #=> index 4 out of matches (IndexError)

m = '2021-12-25'.match(/(?<year>\d+)-(?<month>\d+)-(?<day>\d+)/)
puts m.match_length(:year)  #=> 4
puts m.match_length(:month) #=> 2
puts m.match_length(:day)   #=> 2
puts m.match_length(:foo)   #=> undefined group name reference: foo (IndexError)
```

https://bugs.ruby-lang.org/issues/18172

## Method/UnboundMethod

### メソッドの可視性を確認できるpublic?/protected?/private?が追加された

Ruby 3.1ではMethodオブジェクトとUnboundMethodオブジェクトに対してメソッドの可視性を確認できる`public?`/`protected?`/`private?`メソッドが追加されました。

```ruby
class Sample
  def m1; end
  protected def m2; end
  private def m3; end
end

sample = Sample.new

# Methodオブジェクトに対して可視性を問い合わせる
puts sample.method(:m1).public?    #=> true
puts sample.method(:m2).public?    #=> false
puts sample.method(:m3).public?    #=> false
puts sample.method(:m2).protected? #=> true
puts sample.method(:m3).private?   #=> true

# UnboundMethodオブジェクトに対して可視性を問い合わせる
puts Sample.instance_method(:m1).public?    #=> true
puts Sample.instance_method(:m2).protected? #=> true
puts Sample.instance_method(:m3).private?   #=> true
```

https://bugs.ruby-lang.org/issues/11689

## Module

### 間接的にあるモジュールをすでにprependしていても、同じモジュールが再度prependされるようになった

Ruby 3.1では間接的にあるモジュールをprependしていても、同じモジュールが再度prependされるようになりました。

以下のコード例でRuby 2.7、3.0、3.1の挙動の違いを比較してみます。

```ruby
module Prependable; end
module SuperPrependable; end
module Includable; end
class Sample
  prepend SuperPrependable
  include Includable
end

# ここまでの挙動は同じ
p Sample.ancestors
#=> Ruby 3.1 [SuperPrependable, Sample, Includable, Object, Kernel, BasicObject]
#=> Ruby 3.0 [SuperPrependable, Sample, Includable, Object, Kernel, BasicObject]
#=> Ruby 2.7 [SuperPrependable, Sample, Includable, Object, Kernel, BasicObject]

# Ruby 3.0からはinclude済みのモジュールにあとからprependするとその変更が反映されるようになった
# （Includableモジュールの手前にPrependableが登場している）
Includable.prepend Prependable
p Sample.ancestors
#=> Ruby 3.1 [SuperPrependable, Sample, Prependable, Includable, Object, Kernel, BasicObject]
#=> Ruby 3.0 [SuperPrependable, Sample, Prependable, Includable, Object, Kernel, BasicObject]
#=> Ruby 2.7 [SuperPrependable, Sample, Includable, Object, Kernel, BasicObject]

# Ruby 3.1では間接的にあるモジュールをすでにprependしていても、同じモジュールが再度prependされるようになった
# （Sampleクラスの手前にPrependableモジュールが登場している）
Sample.prepend Prependable
p Sample.ancestors
#=> Ruby 3.1 [Prependable, SuperPrependable, Sample, Prependable, Includable, Object, Kernel, BasicObject]
#=> Ruby 3.0 [SuperPrependable, Sample, Prependable, Includable, Object, Kernel, BasicObject]
#=> Ruby 2.7 [Prependable, SuperPrependable, Sample, Includable, Object, Kernel, BasicObject]

# 直接prependしているモジュールを再度prependしても何も起きない（変更なし）
Sample.prepend SuperPrependable
p Sample.ancestors
#=> Ruby 3.1 [Prependable, SuperPrependable, Sample, Prependable, Includable, Object, Kernel, BasicObject]
#=> Ruby 3.0 [SuperPrependable, Sample, Prependable, Includable, Object, Kernel, BasicObject]
#=> Ruby 2.7 [Prependable, SuperPrependable, Sample, Includable, Object, Kernel, BasicObject]
```

https://bugs.ruby-lang.org/issues/17423

### private/protected/public/module_functionが引数を返すようになった

Ruby 3.1では `private`/`protected`/`public`/`module_function`が引数を返すようになりました。

```ruby
class Sample
  # 引数がないときはnil
  ret = private
  p ret #=> nil

  # 引数が1つならシンボル
  ret = private def foo; end
  p ret #=> :foo

  # 引数が2つ以上ならシンボルの配列
  def bar; end
  def baz; end
  ret = private :bar, :baz
  p ret #=> [:bar, :baz]
end
```

ちなみにRuby 3.0以前では上の`ret`はいずれもSampleクラス（つまり`self`）になります。

https://bugs.ruby-lang.org/issues/12495

## Process

以下の変更点がNEWS.mdに記載されています。

> Process._fork is added. This is a core method for fork(2). Do not call this method directly; it is called by existing fork methods: Kernel.#fork, Process.fork, and IO.popen("-"). Application monitoring libraries can overwrite this method to hook fork events.

詳しい内容については下記リンクを参照してください。

https://bugs.ruby-lang.org/issues/17795

## Struct

### keyword_initオプションを明示的に指定せずに生成されたStructに対してキーワード引数だけでnewしようとすると警告が出るようになった

Ruby 3.1では `keyword_init` オプションを明示的に指定せずに生成されたStructに対してキーワード引数だけでnewしようとすると警告が出るようになりました。具体的には以下のようなケースです。

```ruby
Post = Struct.new(:id, :name)

p Post.new(id: 1, name: "hello")
#=> lib/sample.rb:3: warning: Passing only keyword arguments to Struct#initialize will behave differently from Ruby 3.2. Please use a Hash literal like .new({k: v}) instead of .new(k: v).
#=> #<struct Post id={:id=>1, :name=>"hello"}, name=nil>
```

ただし、次のようなケースは警告が出ません（書き方によって`Post.new`で生成されたオブジェクトの内容が異なる点に注意してください）。

```ruby
Post = Struct.new(:id, :name)

# キーワード引数ではなく、ふつうに2つの引数を渡す場合
p Post.new(1, "hello")
#=> #<struct Post id={:id=>1, :name=>"hello"}, name=nil>
```

```ruby
Post = Struct.new(:id, :name)

# キーワード引数ではなく、引数を{}で囲んで明示的にハッシュを渡す場合
p Post.new({id: 1, name: "hello"})
#=> #<struct Post id={:id=>1, :name=>"hello"}, name=nil>
```

```ruby
Post = Struct.new(:id, :name, keyword_init: true)

# Struct.newで明示的にkeyword_init: trueを指定した場合
# （キーワード引数を指定したことになる）
p Post.new(id: 1, name: "hello")
#=> #<struct Post id=1, name="hello">
```

```ruby
Post = Struct.new(:id, :name, keyword_init: false)

# Struct.newで明示的にkeyword_init: falseを指定した場合
# （1個のハッシュを引数として渡したことになる）
p Post.new(id: 1, name: "hello")
#=> #<struct Post id={:id=>1, :name=>"hello"}, name=nil>
```

```ruby
Post = Struct.new(:id, :name)

# 通常の引数とキーワード引数が混在している場合
# キーワード引数は暗黙的にハッシュに変換される
p Post.new(10, id: 1, name: "hello")
#=> #<struct Post id=10, name={:id=>1, :name=>"hello"}>
```

ちなみに、この警告はRuby 3.2以降で`keyword_init: true`をデフォルトにするための移行措置として発せられるようです。

```ruby
Post = Struct.new(:id, :name)

# Ruby 3.1の場合（警告あり）
p Post.new(id: 1, name: "hello")
#=> #<struct Post id={:id=>1, :name=>"hello"}, name=nil>

# Ruby 3.2の場合（keyword_init: trueがデフォルトになる予定）
p Post.new(id: 1, name: "hello")
#=> #<struct Post id=1, name="hello">
```

https://bugs.ruby-lang.org/issues/16806

### keyword_initオプションの値を確認できるkeyword_init?メソッドが追加された

Ruby 3.1では`Struct.new`したときの`keyword_init`オプションの値を確認できる`keyword_init?`メソッドが追加されました。

```ruby
Foo = Struct.new(:id, :name, keyword_init: true)
p Foo.keyword_init? #=> true

Bar = Struct.new(:id, :name)
p Bar.keyword_init? #=> nil

Baz = Struct.new(:id, :name, keyword_init: false)
p Baz.keyword_init? #=> false
```

https://bugs.ruby-lang.org/issues/18008

## String

### unpackとunpack1でoffsetを指定できるようになった

Ruby 3.1では `unpack`メソッドと`unpack1`メソッドでoffsetを指定できるようになりました。

```ruby
p "ABC".unpack("C*")             #=> [65, 66, 67]
p "ABC".unpack("C*", offset: 0)  #=> [65, 66, 67]
p "ABC".unpack("C*", offset: 1)  #=> [66, 67]
p "ABC".unpack("C*", offset: 2)  #=> [67]
p "ABC".unpack("C*", offset: 3)  #=> []
p "ABC".unpack("C*", offset: -1) #=> offset can't be negative (ArgumentError)

p "ABC".unpack1("C*")             #=> 65
p "ABC".unpack1("C*", offset: 0)  #=> 65
p "ABC".unpack1("C*", offset: 1)  #=> 66
p "ABC".unpack1("C*", offset: 2)  #=> 67
p "ABC".unpack1("C*", offset: 3)  #=> nil
p "ABC".unpack1("C*", offset: -1) #=> offset can't be negative (ArgumentError)
```

https://bugs.ruby-lang.org/issues/18254

### Unicode 13.0.0とEmoji 13.0にアップデート

Ruby 3.1のStringはUnicode 13.0.0とEmoji 13.0にアップデートされました。

https://bugs.ruby-lang.org/issues/17750

https://bugs.ruby-lang.org/issues/18029

## Thread

### ネイティブスレッドのIDを取得できるnative_thread_idメソッドが追加された

Ruby 3.1ではRubyのスレッドが使用しているネイティブスレッドのIDを取得できる`native_thread_id`メソッドが追加されました。

```ruby 
p Thread.main.native_thread_id
#=> 6380411
```

https://bugs.ruby-lang.org/issues/17853

## Thread::Backtrace

### --backtrace-limit オプションの値を取得できる Thread::Backtrace.limit が追加された

Ruby 3.1では`--backtrace-limit`オプションの値を取得できる`Thread::Backtrace.limit`メソッドが追加されました。

```ruby 
# lib/sample.rb
p Thread::Backtrace.limit
```

```
$ ruby --backtrace-limit=10 lib/sample.rb
10

$ ruby lib/sample.rb
-1 
```

ちなみに`--backtrace-limit`オプションはRuby 3.0で追加されたコマンドラインオプションで、エラー発生時に表示されるバックトレースの行数を制限するものです。

```
# バックトレースを制限しない場合
$ ruby test.rb
/Users/jnito/Desktop/test.rb:14:in `f4': undefined local variable or method `f5' for main:Object (NameError)
  from /Users/jnito/Desktop/test.rb:10:in `f3'
  from /Users/jnito/Desktop/test.rb:6:in `f2'
  from /Users/jnito/Desktop/test.rb:2:in `f1'
  from /Users/jnito/Desktop/test.rb:17:in `<main>'
```

```
# バックトレースを2行に制限する場合
$ ruby --backtrace-limit 2 test.rb
/Users/jnito/Desktop/test.rb:14:in `f4': undefined local variable or method `f5' for main:Object (NameError)
  from /Users/jnito/Desktop/test.rb:10:in `f3'
  from /Users/jnito/Desktop/test.rb:6:in `f2'
   ... 3 levels...
```

https://bugs.ruby-lang.org/issues/17479

## Thread::Queue

Ruby 3.1では`Queue#initialize`が初期値としてEnumerableオブジェクトを受け取れるようになりました。

```ruby 
q = Queue.new([1, 2, 3])
p q.pop    #=> 1
p q.pop    #=> 2
p q.pop    #=> 3
p q.empty? #=> true
```

上の例では単純に配列を引数として渡していますが、実際は並列処理で使うworkerオブジェクトを複数渡すユースケースを想定しているようです。

https://bugs.ruby-lang.org/issues/17327

## Time

### Time.newがタイムゾーンをキーワード引数で指定できるようになった

Ruby 3.1では`Time.new`が`in:`というキーワード引数でタイムゾーンを指定できるようになりました。

```ruby
p Time.new(2021, 12, 25, in: "+09:00") #=> 2021-12-25 00:00:00 +0900
```

Ruby 3.0以前ではタイムゾーンは第7引数だったため、タイムゾーンを指定するためには必ず7個の引数を渡す必要がありました。

```ruby
# Ruby 3.0以前ではタイムゾーンは7番目の引数として渡す以外なかった
p Time.new(2021, 12, 25, 0, 0, 0, "+09:00") #=> 2021-12-25 00:00:00 +0900
```

ちなみに、`Time.now`や`Time.at`は以前から`in:`でタイムゾーンを指定することができたため、これに合わせた変更になります。

```ruby
Time.now(in: "+0800")
#=> 2021-12-20 19:29:37.710056 +0800

Time.at(1582721899, in: "+07:00")
#=> 2020-02-26 19:58:19 +0700
```

もう一点、Ruby 3.0までは `Time.new(2021, 12, 25, "+07:00")` のように時間を指定する引数（第4〜6引数）に明らかに数字でない文字列を渡してもエラーなく動作していましたが、Ruby 3.1では整数に変換可能かより厳密に検査されます。

```ruby 
# Ruby 3.0以前： "+07:00"が7時だと見なされる（内部的に"+07:00".to_iを呼んでいる？）
Time.new(2021, 12, 25, "+07:00")
#=> 2021-12-25 07:00:00 +0900

# Ruby 3.1： "+07:00"はエラー
Time.new(2021, 12, 25, "+07:00")
#=> invalid value for Integer(): "+07:00" (ArgumentError)

# "7"であれば文字列でもOK（Ruby 3.0でも3.1でも）
Time.new(2021, 12, 25, "7")
#=> 2021-12-25 07:00:00 +0900
```

https://bugs.ruby-lang.org/issues/17485

### strftimeが不明な地方時を表す"-00:00"をサポートした

Ruby 3.1では`strftime`メソッドが不明な地方時を表す"-00:00"（下記の引用を参照）をサポートしました。

> UTC での時刻はわかるものの地方時が不明なことを示す特別な時差表記 -00:00 があります。
>
> [RFC 3339の日付形式](https://wiki.suikawiki.org/n/RFC%203339%E3%81%AE%E6%97%A5%E4%BB%98%E5%BD%A2%E5%BC%8F)


この表記を使うためには"z"にマイナスのフラグ（"-"）を付けます。

```ruby
time = Time.at(1640000200).gmtime

# 通常の地方時表示
p time.strftime("%Y-%m-%dT%H:%M:%S%z")    #=> 2021-12-20T11:36:40+0000

# マイナスフラグ（%-zなど）を使って不明な地方時を表す
p time.strftime("%Y-%m-%dT%H:%M:%S%-z")   #=> 2021-12-20T11:36:40-0000
p time.strftime("%Y-%m-%dT%H:%M:%S%-:z")  #=> 2021-12-20T11:36:40-00:00
p time.strftime("%Y-%m-%dT%H:%M:%S%-::z") #=> 2021-12-20T11:36:40-00:00:00
```

https://bugs.ruby-lang.org/issues/17544

## TracePoint

> TracePoint.allow_reentry is added to allow reenter while TracePoint callback.
> （筆者訳）TracePointコールバックの最中に再度エントリーする（再度入り込む）TracePoint.allow_reentryが追加された。

とのことですが、サンプルコードは用意できなかったので、以下のIssueを参照してください。

https://bugs.ruby-lang.org/issues/15912

## $LOAD_PATH

### $LOAD_PATH.resolve_feature_pathにloadできない値を渡しても例外が発生しなくなった

Ruby 3.1では `$LOAD_PATH.resolve_feature_path` にloadできない値を渡しても例外が発生しなくなりました（`nil`が返ります）。

```ruby
$LOAD_PATH.resolve_feature_path 'date'
#=> [:rb, "/Users/jnito/.rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/date.rb"]

# loadできない値を渡すとnilが返る
$LOAD_PATH.resolve_feature_path 'dato'
#=> nil

# Ruby 3.0以前では例外が発生していた
$LOAD_PATH.resolve_feature_path 'dato'
#=> cannot load such file -- dato (LoadError)
```

https://bugs.ruby-lang.org/issues/16043

## Fiber Scheduler

以下の6点がNEWS.mdに掲載されています。

- Add support for `Addrinfo.getaddrinfo` using `address_resolve` hook. [[Feature #17370](https://bugs.ruby-lang.org/issues/17370)]
- Introduce non-blocking `Timeout.timeout` using `timeout_after` hook. [[Feature #17470](https://bugs.ruby-lang.org/issues/17470)]
- Introduce new scheduler hooks `io_read` and `io_write` along with a low level IO::Buffer for zero-copy read/write. [[Feature #18020](https://bugs.ruby-lang.org/issues/18020)]
- IO hooks `io_wait`, `io_read`, `io_write`, receive the original IO object where possible. [[Bug #18003](https://bugs.ruby-lang.org/issues/18003)]
- Make `Monitor` fiber-safe. [[Bug #17827](https://bugs.ruby-lang.org/issues/17827)]
- Replace copy coroutine with pthread implementation. [[Feature #18015](https://bugs.ruby-lang.org/issues/18015)]

詳しい内容については上記の各リンクを参照してください。

## Refinement

### refine内でのinclude/prependが非推奨になった

Ruby 3.1ではrefine内で`include`/`prepend`が非推奨になりました。たとえば以下のコードを`-w`オプション付きで実行すると警告が発生します（Ruby 3.2で削除されるようです）。

```ruby
module Shufflable
  def shuffle
    chars.shuffle.join
  end
end

module StringShuffle
  refine String do
    include Shufflable
  end
end

using StringShuffle
puts 'hello'.shuffle
```

```
$ ruby -w lib/sample.rb
lib/sample.rb:10: warning: Refinement#include is deprecated and will be removed in Ruby 3.2
leloh
```

この警告が出る場合は以下のように`include`を`import_methods`に置き換えてください。

```ruby
module StringShuffle
  refine String do
    import_methods Shufflable
  end
end
```

より詳しい内容は[Bug #17429](https://bugs.ruby-lang.org/issues/17429)や以下のスライドの55〜59ページを参照してください。

https://bugs.ruby-lang.org/issues/17429

https://www.docswell.com/s/pink_bangbi/KXWG2Z-2021-12-17-185213#p55

## 標準ライブラリ

### default gemがアップデートされた

Ruby 3.1では以下のdefault gemが更新されました。

<details><summary>更新されたdefault gemを表示する</summary><div>

- RubyGems 3.3.3
- base64 0.1.1
- benchmark 0.2.0
- bigdecimal 3.1.1
- bundler 2.3.3
- cgi 0.3.1
- csv 3.2.2
- date 3.2.2
- did_you_mean 1.6.1
- digest 3.1.0
- drb 2.1.0
- erb 2.2.3
- error_highlight 0.3.0
- etc 1.3.0
- fcntl 1.0.1
- fiddle 1.1.0
- fileutils 1.6.0
- find 0.1.1
- io-console 0.5.10
- io-wait 0.2.1
- ipaddr 1.2.3
- irb 1.4.1
- json 2.6.1
- logger 1.5.0
- net-http 0.2.0
- net-protocol 0.1.2
- nkf 0.1.1
- open-uri 0.2.0
- openssl 3.0.0
- optparse 0.2.0
- ostruct 0.5.2
- pathname 0.2.0
- pp 0.3.0
- prettyprint 0.1.1
- psych 4.0.3
- racc 1.6.0
- rdoc 6.4.0
- readline 0.0.3
- readline-ext 0.1.4
- reline 0.3.0
- resolv 0.2.1
- rinda 0.1.1
- ruby2_keywords 0.0.5
- securerandom 0.1.1
- set 1.0.2
- stringio 3.0.1
- strscan 3.0.1
- tempfile 0.1.2
- time 0.2.0
- timeout 0.2.0
- tmpdir 0.1.2
- un 0.2.0
- uri 0.11.0
- yaml 0.2.0
- zlib 2.1.1

</div></details>

### bundled gemがアップデートされた

Ruby 3.1では以下のbundled gemが更新されました。

- minitest 5.15.0
- power_assert 2.0.1
- rake 13.0.6
- test-unit 3.5.3
- rexml 3.2.5
- rbs 2.0.0
- typeprof 0.21.1

### 一部のdefault gemがbundled gemに変わった

Ruby 3.1では以下のdefault gemがbundled gemに変わりました。Bundlerを使っている場合は明示的にこれらのgemをGemfileに追加する必要があります。

- net-ftp 0.1.3
- net-imap 0.2.2
- net-pop 0.1.1
- net-smtp 0.3.1
- matrix 0.4.2
- prime 0.1.2
- debug 1.4.0

### カバレッジ測定を一時停止できるようになった

Ruby 3.1では`Coverage.suspend`と`Coverage.resume`を使ってカバレッジ測定を一時停止・再開できるようになりました。以下は[Feature #18176](https://bugs.ruby-lang.org/issues/18176)に載っていたサンプルコードの抜粋です。

```ruby
# target.rb
def foo
  :foo
end

def bar
  :bar
end

def baz
  :baz
end
```

```ruby
require "coverage"

# Similar to Coverage.start, but does not start the measurement itself
Coverage.setup(oneshot_lines: true)

load "target.rb"

foo              # This call is not counted
Coverage.resume  # Start the measurement
bar              # This call is counted
Coverage.suspend # Stop the measure
baz              # This call is not counted

# The result is only for Line 7, the body of method "bar"
p Coverage.result #=> {"target.rb"=>{:oneshot_lines=>[7]}}
```

https://bugs.ruby-lang.org/issues/18176

## Random::Formatterモジュールがrandom/formatterライブラリに移動した

Ruby 3.1ではRandom::Formatterモジュールがsecurerandomライブラリからrandom/formatterライブラリに移動しました。そのため、random/formatterライブラリをrequireすればRandomオブジェクトに対して以下のメソッドを呼び出すことができます。

- `hex`
- `random_bytes`
- `base64`
- `urlsafe_base64`
- `uuid`
- `alphanumeric`

```ruby
require 'random/formatter'

p Random.hex
#=> "cc09766ba830589232f1165e962c179f"
p Random.random_bytes
#=> "\xC1m\xC6\xA2^0\xAE{\xD2Xn\xB0;\xFA.\xCF"
p Random.base64
#=> "oMUoHXK/2dTD2lWlFK6IOA=="
p Random.urlsafe_base64
#=> "shWfRtgto5c0ooekLmkOgA"
p Random.uuid
#=> "1d682308-f5ab-4630-9c8b-632247cdbc49"
p Random.alphanumeric
#=> "xmVcnn47AfK4FPdw"
```

Ruby 3.0以前と同様にSecureRandomのクラスメソッドとして呼び出すことも可能です。

```ruby
# 以下のコードはRuby 3.0でも3.1でも有効
require 'securerandom'

p SecureRandom.hex
p SecureRandom.random_bytes
p SecureRandom.base64
p SecureRandom.urlsafe_base64
p SecureRandom.uuid
p SecureRandom.alphanumeric
```

https://bugs.ruby-lang.org/issues/18190

## 標準ライブラリの後方互換性問題

### ERB#initialize の第2引数以降が常に警告を出すようになった

`ERB#initialize`（`new`メソッド）のシグニチャは以下のようになっています。（[公式リファレンス](https://docs.ruby-lang.org/ja/latest/class/ERB.html#S_NEW)から抜粋）

```
new(str, safe_level=NOT_GIVEN, trim_mode=NOT_GIVEN, eoutvar=NOT_GIVEN, trim_mode: nil, eoutvar: '_erbout') -> ERB
```

Ruby 3.1では`ERB#initialize`に2つ以上の引数を渡すと`-w`オプション無しでも警告が出るようになりました（Ruby 3.0では`-w`を付けないと警告が出なかった）。

```ruby
require 'erb'

ERB.new("Hello.", 0)
#=> warning: Passing safe_level with the 2nd argument of ERB.new is deprecated. Do not use it, and specify other arguments as keyword arguments.
```

警告メッセージにもあるとおり、第2引数の`safe_level`（セーフレベル）はすでに廃止された仕組みなので使うことはできません。

https://imaizumimr.hatenablog.com/entry/2020/12/05/000000

第3引数の`trim_mode`や第4引数の`eoutvar`を使いたい場合は、同名のキーワード引数を使うようにしてください。

https://bugs.ruby-lang.org/issues/14256

### ppメソッドがターミナルの幅を考慮するようになった

Ruby 3.1ではppメソッドがターミナルの表示幅を考慮して出力フォーマットを切り替えるようになりました。以下はirb上でppメソッドを呼びだしたときの実行結果です。

```
# 画面幅が狭いとき
irb(main):001:0> pp Encoding.aliases.take(5)
[["BINARY", "ASCII-8BIT"],
 ["CP437", "IBM437"],
 ["CP720", "IBM720"],
 ["CP737", "IBM737"],
 ["CP775", "IBM775"]]

# 画面幅が広いとき
irb(main):002:0> pp Encoding.aliases.take(5)
[["BINARY", "ASCII-8BIT"], ["CP437", "IBM437"], ["CP720", "IBM720"], ["CP737", "IBM737"], ["CP775", "IBM775"]]
```

Ruby 3.0では画面幅が広くても上の「狭いとき」と同じ形式で出力されます。

```
# Ruby 3.0では画面幅が広くても狭くても出力形式が同じ
irb(main):002:0> pp Encoding.aliases.take(5)
[["BINARY", "ASCII-8BIT"],
 ["CP437", "IBM437"],
 ["CP720", "IBM720"],
 ["CP737", "IBM737"],
 ["CP775", "IBM775"]]
```

https://bugs.ruby-lang.org/issues/12913

### Psych.loadがデフォルトでPsych.safe_loadと同等の挙動になった

Ruby 3.1ではPsychのバージョンが3系から4系に上がりました。Psych 4.0では`Psych.load`がデフォルトで`Psych.safe_load`と同等の挙動になりました。

4系でエラーが出る場合はPsych 3.3.2をインストールし、`Psych.load`を`Psych.unsafe_load`に置き換えると、いったん3系の挙動を維持したまま4系にアップグレードすることができます。

https://github.com/ruby/psych/commit/cb50aa8d3fb8be01897becff77b4922b12a0ab4c

この変更点に関して詳しい内容を知りたい場合は以下の記事やIssueを参照してください。

https://secret-garden.hatenablog.com/entry/2021/05/23/200803

https://bugs.ruby-lang.org/issues/17866

## パフォーマンス改善等

パフォーマンス改善については以下の5点がNEWS.mdに掲載されています。

- Inline cache mechanism is introduced for reading class variables. [[Feature #17763](https://bugs.ruby-lang.org/issues/17763)]
- `instance_eval` and `instance_exec` now only allocate a singleton class when required, avoiding extra objects and improving performance. [[GH-5146](https://github.com/ruby/ruby/pull/5146)]
- The performance of `Struct` accessors is improved. [[GH-5131](https://github.com/ruby/ruby/pull/5131)]
- `mandatory_only?` builtin special form to improve performance on builtin methods. [[GH-5112](https://github.com/ruby/ruby/pull/5112)]
- Experimental feature Variable Width Allocation in the garbage collector. This feature is turned off by default and can be enabled by compiling Ruby with flag `USE_RVARGC=1` set. [[Feature #18045](https://bugs.ruby-lang.org/issues/18045)] [[Feature #18239](https://bugs.ruby-lang.org/issues/18239)]

詳しい内容については上記の各リンクを参照してください。

## JIT

### コマンドオプションの意味が変わった

Ruby 3.1ではMJITに加えてYJITが実験的に導入されたため、コマンドオプションの意味が一部変わりました（以下はNEWS.mdの内容を意訳したものです）。

- Ruby 3.0以前の`--jit`オプションは`--mjit`にリネームされた
- Windows x86-64プラットフォームでは`--jit`が`--mjit`のエイリアスになった
- それ以外のプラットフォーム（macOS や Linux）では`--jit`が`--yjit`のエイリアスになった

### MJITの各種変更

MJITに関しては以下の4点がNEWS.mdに掲載されています。

- The default `--mjit-max-cache` is changed from 100 to 10000.
- JIT-ed code is no longer cancelled when a TracePoint for class events is enabled.
- The JIT compiler no longer skips compilation of methods longer than 1000 instructions.
- `--mjit-verbose` and `--mjit-warning` output "JIT cancel" when JIT-ed code is disabled because TracePoint or GC.compact is used.

### YJITの実験的導入

Ruby 3.1ではYJITと呼ばれる新しいJITコンパイラが実験的に導入されました。YJITを有効化する方法や特長は以下のとおりです（以下はNEWS.mdの内容を意訳したものです）。

- `--yjit`オプションを付けて実行するとYJITが有効化されます
- YJITを使うと[railsbench](https://github.com/k0kubun/railsbench)で最大22%、[liquid-render](https://shopify.github.io/liquid/)で39%高速化されます
- 短時間でウォームアップが完了します
- 現時点ではUnixライクな環境（macOS や Linux）のみをサポートします

YJITに関する詳しい情報は以下の資料を参考にしてください。

https://techracho.bpsinc.jp/hachi8833/2021_11_02/112909

https://bugs.ruby-lang.org/issues/18229

## 静的解析

### RBS

（以下はNEWS.mdの内容を意訳したものです）

- ジェネリックな型パラメータに制限が付けられるようになりました。（[PR](https://github.com/ruby/rbs/pull/844)）

```
# `T` は `_Output` インターフェースと互換性がなければいけない
# `PrettyPrint[String]` は良いが `PrettyPrint[Integer]` は型エラーとなる
class PrettyPrint[T < _Output]
  interface _Output
    def <<: (String) -> void
  end
  attr_reader output: T
  def initialize: (T output) -> void
end
```

- ジェネリックな型エイリアスが定義できるようになりました。（[PR](https://github.com/ruby/rbs/pull/823)）

```rbs
# ジェネリックな `list` 型を定義する
type list[T] = [ T, list[T] ] | nil

type str_list = list[String]
type int_list = list[Integer]
```

- gemのRBSを管理するために[rbs collection](https://github.com/ruby/rbs/blob/master/docs/collection.md)が導入されました。
- 組み込みライブラリや標準ライブラリのシグニチャが多数追加・更新されました。
- 多数のバグ修正やパフォーマンス改善が行われました。

詳細は[RBSのCHANGELOG.md](https://github.com/ruby/rbs/blob/master/CHANGELOG.md)を参考にしてください。

### TypeProf

（以下はNEWS.mdの内容を意訳したものです）

- [実験的なIDEサポート機能](https://github.com/ruby/typeprof/blob/master/doc/ide.md)が追加されました。
- 多数のバグ修正やパフォーマンス改善が行われました。

TypeProfのIDEサポート機能についてはRubyKaigi Takeout 2021の以下のスライドや動画も参照してください。

https://rubykaigi.org/2021-takeout/presentations/mametter.html

## デバッガ

（以下はNEWS.mdの内容を意訳したものです）

Ruby 3.1では新しいデバッガとして[debug.gem](https://github.com/ruby/debug)が同梱されました。debug.gemは高速で、リモートデバッグや色づけされたREPL、IDE（VS Code）統合機能といった様々な機能を提供します。これまで使われていた標準ライブラリの`lib/debug.rb`はdebug.gemの置き換えられました。

デバッグ実行を開始・制御するための`rdbg`コマンドも`bin/`ディレクトリにインストールされます。

debug.gemに関して詳しい情報を知りたい場合は以下のような記事を参考にしてみてください。

https://techracho.bpsinc.jp/hachi8833/2021_09_03/111137

https://secret-garden.hatenablog.com/entry/2021/10/13/223925

https://zenn.dev/leaner_tech/articles/20210915-rubykaigi-2021-debug-gem

https://qiita.com/yuskubo/items/111a87e2402de8e12914

## エラーハイライト（error_highlight）

（以下はNEWS.mdの内容を意訳＆サンプルコードを一部改変したものです）

Ruby 3.1ではerror_highlightと呼ばれるビルトインgemが導入されました。このgemはバックトレース内でエラーの発生箇所を詳細に表示します。

以下はサンプルコードとエラーの表示例です。

```ruby
json = nil

title = json[:article][:title]
#=> lib/sample.rb:3:in `<main>': undefined method `[]' for nil:NilClass (NoMethodError)
#   
#   title = json[:article][:title]
#               ^^^^^^^^^^
```

```ruby
json = {article: nil}

title = json[:article][:title]
#=> lib/sample.rb:3:in `<main>': undefined method `[]' for nil:NilClass (NoMethodError)
#   
#   title = json[:article][:title]
#                         ^^^^^^^^
```

この機能はデフォルトで有効化されています。無効化する場合は`--disable-error_highlight`というコマンドラインオプションを付けます。詳しい使い方は[error_highlightのリポジトリ](https://github.com/ruby/error_highlight)を参照してください。

### エラーハイライトの制限事項

この記事を執筆している時点ではエラーハイライトには以下のような制限事項（known issue）があるようです。

- 非Ascii文字（全角文字）が含まれるとハイライトがズレる
- 1行の文字が長すぎるとうまくハイライトできない
- irb上では動作しない（無効化される）

詳しい内容はそれぞれ以下のissueをご覧ください。

https://github.com/ruby/error_highlight/issues/4

https://github.com/ruby/error_highlight/issues/5

https://github.com/ruby/error_highlight/issues/6

## IRB

### IRB上で自動補完とドキュメント表示ができるようになった

（以下はNEWS.mdの内容を意訳したものです）

IRBに自動補完（オートコンプリート）機能が実装されました。コードをタイプするとダイアログに自動補完の候補が表示されます。タブキーもしくはシフトタブキーで候補を上下に選択できます。

ドキュメントがインストールされていれば、補完候補を選択した際にドキュメントダイアログが自動補完ダイアログの隣に表示されます。表示されるのはドキュメントの一部ですが、Alt-Dでドキュメント全文を読むことができます。

![Screen Shot 2021-12-25 at 18.14.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/f0f5c43e-8ba6-b918-a2ac-7e42f7e6851d.png)

## その他の変更点

### ファイナライザ内で例外が発生すると標準エラー出力に出力されるようになった

Ruby 3.1ではファイナライザ（オブジェクトが GC される時に呼び出される処理）内で例外が発生すると標準エラー出力にその内容が出力されるようになりました。

以下は筆者が実際に遭遇した例外メッセージの例です（[参考](https://github.com/vcr/vcr/pull/907)）。

```
/Users/jnito/.rbenv/versions/3.1.0-preview1/bin/bundle: warning: Exception in finalizer #<Proc:0x000000010a01e6f8 /Users/jnito/.rbenv/versions/3.1.0-preview1/lib/ruby/gems/3.1.0/gems/vcr-6.0.0/lib/vcr/library_hooks/webmock.rb:36 (lambda)>
/Users/jnito/.rbenv/versions/3.1.0-preview1/lib/ruby/gems/3.1.0/gems/vcr-6.0.0/lib/vcr/library_hooks/webmock.rb:36:in `block in global_hook_disabled_requests': wrong number of arguments (given 1, expected 0) (ArgumentError)
``` 

この例外を表示しないようにするためには[`$VERBOSE`](https://docs.ruby-lang.org/ja/latest/method/Kernel/v/=2dv.html)に`nil`をセットするか、`-W0`オプションを付けてプログラムを実行します。

```
# -W0オプションを付けて実行するコマンド例
RUBYOPT=-W0 bundle exec rspec
```

### その他

NEWS.mdのMiscellaneous changes欄にはこのほかに以下の3点が掲載されています。

- lib/objspace/trace.rb is added, which is a tool for tracing the object allocation. Just by requiring this file, tracing is started immediately. Just by Kernel#p, you can investigate where an object was created. Note that just requiring this file brings a large performance overhead. This is only for debugging purposes. Do not use this in production. [[Feature #17762](https://bugs.ruby-lang.org/issues/17762)]
(https://bugs.ruby-lang.org/issues/17798)]
- ruby -run -e httpd displays URLs to access. [[Feature #17847](https://bugs.ruby-lang.org/issues/17847)]
- Add ruby -run -e colorize to colorize Ruby code using IRB::Color.colorize_code.

詳しい内容については上記の各リンクを参照してください。

## 公式リファレンスのURLが変わった

（2022.1.10追記）

Ruby 3.1から（正確には3.0から）公式リファレンスマニュアル（るりま）のURLが変わりました。

- OLD https://docs.ruby-lang.org/ja/2.7.0/doc/index.html
- NEW https://docs.ruby-lang.org/ja/3.1/doc/index.html

以前は"2.7.0"のようにURLにTEENYバージョンが含まれていましたが、Ruby 3.1では"3.1"のようにマイナーバージョンまでになりました。

なお、Ruby 3.0では"3.0.0"にアクセスすると"3.0"にリダイレクトされるようになっています。

https://docs.ruby-lang.org/ja/3.0.0/doc/index.html
↓ リダイレクト 
https://docs.ruby-lang.org/ja/3.0/doc/index.html

## まとめ

というわけで、この記事ではRuby 3.1の変更点と新機能をいろいろと紹介してみました。

個人的にはハッシュリテラルやキーワード引数で値が省略できるようになったのが、Ruby 3.1の一番の目玉じゃないかな〜と思っています（普段のコーディングが結構変わりそう、という意味で）。それと、irbもすごくリッチになっていて、ちょっとした動作確認がとても便利になりそうです。

今年も2021年のクリスマスにRuby 3.1を届けてくれたMatzさんやコミッタのみなさんに感謝したいと思います。どうもありがとうございました！
みなさんもぜひRuby 3.1の新機能を試してみてください😉

### PR: 拙著「プロを目指す人のためのRuby入門」の改訂2版が発売されました🍒

Ruby 3.1がリリースされるちょっと手前、2021年12月2日に拙著「プロを目指す人のためのRuby入門」（通称・チェリー本）の改訂2版が発売されました。第1版の対象バージョンはRuby 2.4でしたが、改訂2版ではRuby 3.0をフルサポートしています。特に、Ruby 2.7から導入されたパターンマッチについては、新しく章を追加して基本から発展的な内容まで詳細に説明しています。

https://gihyo.jp/book/2021/978-4-297-12437-3

その他、改訂2版の変更点については以下のブログ記事で詳しく説明しています。

https://blog.jnito.com/entry/2021/12/02/072804

前述の通り、本書の対象バージョンはRuby 3.0ですが、Ruby 3.1以降で発生する記述内容との差異は、それぞれ以下の記事にまとめてあります。なので、多少バージョンが古くても安心して読んでいただけます😊

https://qiita.com/jnchito/items/bbabb21a1ce3f4799b52