🏀

【Ruby】ぼっち演算子との付き合い方

に公開

こんにちは、またははじめまして、Tamaです。
過去の案件内でいただいたフィードバックを振り返っていたのですが、その中でもぼっち演算子の使い方についてコメントをいただく機会が多かったように感じました。
技術記事でぼっち演算子の使い方について解説した記事はあれど、使いどころについて解説した記事はほぼありませんでした。
自分の理解度向上のためにも、ぼっち演算子との付き合い方についてまとめます。

ぼっち演算子とは

&.演算子のことです。
Ruby2.3から実装された機能で、正式にはsafe navigation operatorという名前が付けられています。&の部分がひとりぼっちで膝を抱えた人のように見えることから、通称lonely operator(ぼっち演算子、ぼっちオペレーター)と呼ばれています。
以降この記事では&.演算子をぼっち演算子と呼びます。

ぼっち演算子の基本の使い方

nilかもしれないオブジェクトに対して、安全にメソッドを呼び出したい場合 に使えます。

a = nil

# nilオブジェクトにupcaseメソッドを使用するとNoMethodErrorになる
a.upcase
# => undefined method `upcase' for nil:NilClass (NoMethodError)

# ぼっち演算子を使用すると、オブジェクトがnilであれば、nilを返す
a&.upcase
# => nil

# nil以外のオブジェクトなら、b.upcaseと書いたときと同じ結果を返す
b = 'hoge'
b&.upcase
# => "HOGE"

つまり、ぼっち演算子を使ってメソッドを呼び出すと、対象がnilでなければメソッドを実行し、nilであればnilを返します。

ぼっち演算子はどんどん使うべき?

結論から言うと、乱用は避けるべき です。
便利な一方、以下のような問題を引き起こす可能性があります。

ぼっち演算子を使うことによる懸念

1. バグの温床になりやすい(意図しないnil許容)

&.を使用すると、 nilを許容して処理をスキップ するため、本来エラーを起こすべき状況を見逃すことがあります。

2. コードの意図が不明瞭になる

ぼっち演算子を多用すると、「本当にnilでも良いのか」「どの時点でnilが許容されるのか」などが不明瞭になります。
一見便利ですが、仕様が見えにくくなります。

3. 設計の曖昧さが隠れる

そもそもnilが帰ってくるような構造にしていること自体が「オブジェクトの責務が曖昧」である可能性があります。

4. テストが甘くなりやすい

ぼっち演算子を多用すると、「nilの場合どうなるか」のテストが漏れやすくなります。
また、nilケースを深く考えなくなるため、意図しない振る舞いが本番で起こるリスクがあります。

それでも使うべき場面

もちろん、使うべき場面もあります。

  • nil が自然に発生し得る構造であり、nil を明示的に許容する仕様になっている場合
  • 記述を簡潔にしたい場合
# userが存在しないこともあるが、明示的にnil許容したい場面
User.find_by(id: params[:user_id])&.profile&.bio

このような場面では、可読性と安全性のバランスをとって、ぼっち演算子を適切に使うのが望ましいです。

ぼっち演算子を回避する

私の場合、動作確認でNoMethodErrorになるととりあえずぼっち演算子を使ってしまう癖がありました。
NoMethodErrorが出てしまった時に、ぼっち演算子を使用する以外の回避方法をいくつか挙げます。

パターン1: 条件分岐で明示的にチェック

user&.profile&.bio

は次のようにも書けます:

if user && user.profile
 user.profile.bio
end

パターン2: ガード節で事前にnilチェック

def show_bio(user)
  return unless user
  return unless user.profile

  user.profile.bio
end

パターン3: デフォルト値で nil を回避

@array = Hoge.array
@array&.find { |char| ... }

ではなく、最初から空配列を代入しておく:

@array = Hoge.array || []
@array.find { |char| ... }

このように書くことで、後続の処理が安全になります。

パターン4: Null Objectパターン

Null Object パターンとは、「値がない状態」を表現するために特別なクラスを用意し、nilを返さないようにする設計手法です。これにより、呼び出し元でnilチェックが不要になり、コードの意図が明確になります。

class NullProfile
  def bio
    ''
  end
end

class User
  def profile
    @profile || NullProfile.new
  end
end

これで呼び出し元で安全にuser.profile.bioを使えます。
ただし、Null Objectも通常のオブジェクトのように振る舞うため、ログやデバッグ時に「データがある前提で動いているように見えるけど、実は何も設定されていない」という混乱が起きる可能性があるなど、デメリットもあります。

まとめ:ぼっち演算子を使う前に考えたいこと

  • その nil本当に許容されるべきものか?
  • nil のまま処理を進めて問題ないか?(ログ出力など)
  • nil によって後続処理に悪影響が出る可能性はないか?
  • 本来は設計やデータ構造を見直すべきではないか?

ぼっち演算子は便利ですが、設計の問題や意図の曖昧さを隠してしまう可能性があります。
仕様の意図が明確な場合にのみ、慎重に使用しましょう

最後に

ぼっち演算子を使う・使わないの判断はまだ難しいのですが、ぼっち演算子の使用は可能なら避けた方が良いという認識をもってコーディングをしていきたいです。
今後も日々の業務で得た気づきを積極的にアウトプットしていければと思っています。
「ぼっち演算子、ちょっと使いすぎかも?」と思った方の参考になれば幸いです。

参考:

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

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

https://ytnk531.hatenablog.com/entry/2020/10/31/085457

ラグザイア

Discussion