Ruby puts の返り値は nil

毎週日曜日に参加しているパーフェクトRails輪読会。9月6日に、gsubの話が出ていて、自分も頭が混乱したので、整理してみる。

第5回パRails輪読会ノート(2020-09-06) - HackMD

String#gsub (Ruby 2.7.0 リファレンスマニュアル)

gsubは、文字列を置き換えるメソッド。

次の例では、b0に置き換えている。

irb(main):001:0> 'abcabc'.gsub(/b/, '0')
=> "a0ca0c"

下のように、ブロックを渡せることを知らなかった。

irb(main):002:0> 'abcabc'.gsub(/b/) { |str| str.upcase }
=> "aBcaBc"

自分でも動かしてみたのだが、これをやってみて混乱した。

irb(main):003:0> 'abcabc'.gsub(/[bc]/) { |str| p str }
"b"
"c"
"b"
"c"
=> "abcabc"

irb(main):004:0> 'abcabc'.gsub(/[bc]/) { |str| puts str }
b
c
b
c
=> "aa"

上の例(p)では返り値が"abcabc"で、下の例(puts)では"aa"になっている。

結論から言うと、putsの返り値がnilだからだ。

irb で見てみる。

irb(main):023:0> p nil
nil
=> nil
irb(main):024:0> puts nil

=> nil

'abcabc'.gsub(/[bc]/) { |str| puts str }では、/[bc]/にマッチするbcが、puts strnilに置き換えられるので、返り値が"aa"になる。

一方、pは引数をそのまま返すので、b が b、c が c に置き換えられて(つまり何も置き換えないのと同じ)、全体の返り値は"abcabc"になる。

Kernel.#puts (Ruby 2.7.0 リファレンスマニュアル)

Kernel.#p (Ruby 2.7.0 リファレンスマニュアル)

余談

putsnilを返すことは知ってはいたのだが、こういう動きになることを理解していなかった。

今回のことがあって、改めて オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル (Ruby 2.7.0 リファレンスマニュアル)putsp の項目を読んだ。

それでまたまた混乱したのが、#=>という記号が、標準出力結果を表す場合と、返り値を表す場合があるということ。

たとえば、p のサンプルコード。#=>は、標準出力結果を表している。

puts "" #=> (空行)
p "" #=> ""

puts 50,"50"
#=> 50
#=> 50
p 50,"50"
#=> 50
#=> "50"

一方、String#empty? のサンプルコード。#=>は、返り値を表している。

"hello".empty?   #=> false
" ".empty?       #=> false
"".empty?        #=> true

#=>についての説明があった。

p a #=> 1 の #=> って何?
「#=>」は標準出力に出力されるということを表しています。「p a #=> 1 」は「p a」を実行すると標準出力に「1」が出力されるという意味です。
このマニュアルのヘルプ (Ruby 2.7.0 リファレンスマニュアル)


xxx #=> 実行結果
xxx # => 実行結果
慣用的に実行結果を示すために使われるコメントの書き方。
Rubyで使われる記号の意味(正規表現の複雑な記号は除く) (Ruby 2.7.0 リファレンスマニュアル)

ということなので、間違いではなさそう。流れで見極めましょうということなのかな。