RubyのNull条件演算子(ぼっち演算子)の挙動に引っかかった話
最近の(何らかの形でNullを扱う)プログラミング言語の多くは、Null条件演算子と呼ばれるタイプの演算子を備えていることが多いです。 たとえば、私が3年近く仕事をしてきた.NETな世界の代表言語であるC#でNull条件演算子を使うとこんな具合です。
code: csharp
using System;
using System.Collections.Generic;
namespace NullConditionTest
{
class Program
{
static void Main(string[] args)
{
var lst = new List<string>();
lst.Add("Hoge");
lst.Add(null); // ここでnullをセット
lst.Add("FugaFuga");
foreach(var e in lst) {
// "e?." の "?."が Null条件演算子
Console.WriteLine(e?.Length); // nullのときはConsole.WriteLine()と同じ挙動
}
}
}
}
// 出力は次の通り
// 4
//
// 8
さて、RubyもNull条件演算子ともいうべき演算子を備えていて、&.と書きます。
(この見た目が、「ひとりぼっちの誰かが地面に何か書きながらいじけている様子に見える」ので、ぼっち演算子と銘されているそうです。)
先のC#の例と似たようなコードをRubyで書くと、こんな感じになります。
code: ruby
lst.each do |e|
p e&.size
end
# 出力は次の通り
# 4
# nil
# 8
Rubyでは、出力の通りnilが出ているのがミソで、メソッドチェインさせたときに大きな違いが出てきます。
C#では
code: csharp
e?.ToUpper().ToLower().Length
とやると、eがnullだった場合、後続のToUpperはもちろん、ToLowerやその先につながるメソッドの呼び出しは行われません。
しかし、Rubyでは
code: ruby
e&.upcase.downcase.size
とやるとupcaseの呼び出しこそ行われませんが、その結果がnilに評価されてしまうため、downcaseメソッドの呼び出しで例外が発生します。
この挙動にハマって、「なんでオブジェクトがnilの場合にその先のメソッドで変な例外が出てくるんだろう?
」と数時間を無駄にしてしまいました。
意図通り動かすには
code: ruby
e&.upcase&.downcase&.size
と書いてやる必要があって、見た目がまったくスマートでなくなります。
RubyのNull条件演算子は、Null条件演算子にあってNull条件演算子にあらずという気がしています。