Ruby 3.1で発生する「プロを目指す人のためのRuby入門 改訂2版」との差異について

## はじめに

2021年12月25日に、Rubyの新しいバージョンであるRuby 3.1がリリースされました。
一方、2021年12月2日に出版した書籍「[プロを目指す人のためのRuby入門 改訂2版](https://gihyo.jp/book/2021/978-4-297-12437-3)」（通称・チェリー本。以下、本書）は執筆当時最新だったRuby 3.0を対象にしています。

本書は紙の本であるため、簡単に内容をアップデートすることができません。しかし、何もしないとどんどん内容が古くなってしまい、「本の通りやってみたけど、今使っているRubyとなんか動きが違う」ということになってしまいます。

そこで新しいRubyのバージョンがリリースされて、本書の説明と異なる部分が出てきたときは、毎回ネット上でその差異を説明するようにしています。その説明を読めば、動きが違う部分があってもきっと落ち着いて対処できるはず、という算段です。

というわけで、この記事ではRuby 3.1で発生する「プロを目指す人のためのRuby入門 改訂2版」との差異について説明します（第1版との差異ではないのでご注意ください）。

なお、本文に出てくる章番号や項番号は、書籍の中で使われている番号です。

## 本文の説明と実行結果が異なるもの

以下で説明する内容は、Ruby 3.1で実行した場合に本書（つまりRuby 3.0）で説明している内容と実行結果が異なる部分です。

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

（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

### IRB上で自動補完とドキュメント表示ができるようになった（第2章 P49のコラム「Ruby 2.7以降で使えるirbの便利機能」）

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/df058441-fa1b-f2b9-8313-37a8c70a63d4.png)

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

Ruby 3.1ではppメソッドがターミナルの幅を考慮するようになりました。そのため、ターミナルの幅が広いと本書に記載した実行結果と異なる場合があります。

```
# ターミナルの幅が狭いとき
pp Encoding.aliases.take(5)
[["BINARY", "ASCII-8BIT"],
 ["CP437", "IBM437"],
 ["CP720", "IBM720"],
 ["CP737", "IBM737"],
 ["CP775", "IBM775"]]

# ターミナルの幅が広いとき
pp Encoding.aliases.take(5)
[["BINARY", "ASCII-8BIT"], ["CP437", "IBM437"], ["CP720", "IBM720"], ["CP737", "IBM737"], ["CP775", "IBM775"]]
```

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

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

```ruby
def find_name = 'Alice'

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

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

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

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

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

```ruby
@foo = 'Alice'

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

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

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
```

ただし、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!
```

### debug.gemがインストール不要になった（12.4.4）

Ruby 3.1ではdebug.gemが同梱されるようになったため、`gem install debug`のようにしてdebug.gemをインストールする必要がなくなりました。

```
# Ruby 3.1ならgem install debugが不要
$ rdbg -v
rdbg 1.4.0
```

### エラーハイライト（error_highlight）が導入された（本書全般、特に第12章）

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]
#                         ^^^^^^^^
```

ただし、エラーハイライトはirb上では有効にならないため、上記の結果を得るためにはファイルに保存したRubyプログラムをrubyコマンドで実行する必要があります。

同じ理由で12章で紹介しているエラー発生時のバックトレース表示も以下のように変わっています。

```
# 本書の記述（12.2）
$ ruby lib/backtrace_sample.rb
 /(プログラムのパス)/lib/gate.rb:24:in `calc_fare': undefined local variable or method `distanse' for #<Gate:0x00000001058d44f0 @name=:mikuni> (NameError)
Did you mean?  distance
        from /(プログラムのパス)/lib/gate.rb:14:in `exit'
        from lib/backtrace_sample.rb:9:in `<main>'

# Ruby 3.4
$ ruby lib/backtrace_sample.rb 
/(プログラムのパス)/lib/gate.rb:24:in `calc_fare': undefined local variable or method `distanse' for #<Gate:0x0000000102fb4598 @name=:mikuni> (NameError)

    FARES[distanse - 1]
          ^^^^^^^^
Did you mean?  distance
	from /(プログラムのパス)/lib/gate.rb:14:in `exit'
	from lib/backtrace_sample.rb:9:in `<main>'
```

Ruby 3.4だと「エラーが起きてるのは `FARES[distanse - 1]` の`distanse`ですよ」と最初から教えてくれるので、ほとんどネタバレになっちゃってますね（苦笑）。

### Steepの型チェックが少し厳しくなった（13.10.2）

これは厳密にいうとRuby 3.1とは関係ないのですが、Steepのバージョンが0.51.0以上だとSteepの仕様変更が原因で本書のとおりにサンプルコードを書いてもエラーが発生する場合があります。

Steepのバージョンは以下のコマンドで確認できます。

```
$ steep --version
1.2.0
```

Steep 0.51.0以上で発生するエラーとその解決方法は以下の記事で詳しく説明しているので、こちらをご覧ください。

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

## Ruby 3.1で追加された新しい言語仕様

このほかにもRuby 3.1では、本書執筆時点にはなかった新しい構文がいくつか追加されています。

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


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')
```

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


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}
```

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


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(&)
```

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

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
```

## Ruby 3.1で導入されたその他の新機能について

この記事では「プロを目指す人のためのRuby入門 改訂2版」と関連が深い変更点をリストアップしましたが、Ruby 3.1ではこのほかにも様々な新機能があります。Ruby 3.1の新機能については以下の記事にまとめたので、興味がある人はチェックしてみてください。

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



## まとめ

というわけで、この記事ではRuby 3.1で発生する「プロを目指す人のためのRuby入門 改訂2版」との差異をまとめました。

まだマイナーバージョン1つ分しか変わっていないので、Ruby 3.1で動かすと書籍の内容と異なる、という部分はかなり限定的ですね。とはいえ、新しい言語仕様のセクションで紹介した「ハッシュリテラルとキーワード引数で値を省略できるようになった」などは、実用的かつ大きな仕様変更なので、なるべく早くキャッチアップしたい変更点だと思います。

「プロを目指す人のためのRuby入門 改訂2版」とこの記事をあわせて読めば、Ruby 3.1でもバリバリと実践的なコードが書けるはずです。まだ「プロを目指す人のためのRuby入門 改訂2版」を購入されていない方は、この機会にぜひ購入を検討してもらえると嬉しいです。みなさん、よろしくお願いします！

### PR：「プロを目指す人のためのRuby入門 改訂2版」について

「プロを目指す人のためのRuby入門 改訂2版」は、他の言語での開発経験があり、これからRubyを始めたい人や、Rubyプログラミングの経験はある程度あるものの、まだまだ自信がない人に向けて、Rubyの言語仕様や開発の現場で役立つ知識を詳しく、ていねいに解説したRubyの入門書です。

改訂2版ではRuby 3.0に完全対応し、「パターンマッチ」の章を新たに追加するなど、第1版に比べてさらに内容が充実しています。

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

改訂2版の変更点については以下のブログ記事で詳しく説明していますので、こちらのぜひチェックしてみてください。

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