Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

私はnilを使わない(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

私はnilを使わない(翻訳)

私はnilを使いません。あるいは少なくとも使わないよう努めています。

NoMethodError: undefined method 'some_method' for nil:NilClass

上はproductionシステムで実によく見かけるエラーのひとつです。このエラーは、null値を参照可能な変数ならどこでも発生する可能性があります。本記事ではこのトピックについていくつかのケーススタディを述べます。

マイグレーション

私がテーブルを作成または更新するときには、常にnull: false制約とデフォルト値の追加を検討するようにしています。文字列値の場合デフォルトは空文字列に、数値の場合はデフォルト値はゼロにします。

こうすることでデータベースレベルでチェックされるようになり、カラムがnilになる心配なしに操作できるようになります。たとえば、ブログシステムで以下のPostモデルを例に考えてみましょう。

# db/migrate/...
class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :posts do |t|
      # [snip]

      # 悪くない
      t.string :excerpt

      # さらによい
      t.string :excerpt, null: false, default: ''
    end
  end
end
# app/views/posts/show.html.erb
<p>
  <%= @post.excerpt.downcase %>
</p>

特にこのコード例では、値が常にStringオブジェクトを返すようになっているので、Post#excerptdowncaseを送信すると常に通ってしまいます。

enum

次に、任意のpostが何らかのカテゴリを持てるとしましょう。これらのカテゴリはそれ以外の情報や振る舞いを持たないので、Postモデルでenum属性を使うことにします。

# db/migrate/...
class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :posts do |t|
      # [snip]

      # 悪くない
      t.integer :category

      # さらによい
      t.integer :category, null: false, default: 0
    end
  end
end

ここで、デフォルトのenumオプションを追加すれば簡単に「カテゴリなしのpost」を表現できます。そうする方がよい理由とは何でしょうか?Post#categoryメソッドが返すオブジェクトの型が常に同じになるからです。こうすることで、カテゴリを扱う場合にコードでのnilチェックが回避されますし、この属性を扱うときのぼっち演算子(safe navigation operator)が不要になります。

# app/models/post.rb
class Post < ApplicationRecord
  enum category: { general: 0, lifestlye: 1, art: 2, misc: 3 }
end

上のコード例では、デフォルト値としてgeneralというカテゴリが用いられます。

NullObject

NullObjectによるアプローチはnull値をエレガントに表現できます。この方法はある意味で上のenumのケーススタディと似ています。このパターンについてはthoughtbotの良記事があります。

エラー: nil:NilClassにはconclusionメソッドが定義されていません

記事のまとめです。私がnilを使わない理由は、プログラムでNoMethodErrorエラーをraiseして欲しくないからです。これを回避するのは、コードベースでnull値を扱わないようにするのと同じぐらい簡単です。

最後に、nilの発明者であるTony Hoareの言葉を引用します。

私が「数十億ドルの過ち」と呼んでいるのは、1965年に発明されたnull参照です。当時のわたしは、とあるオブジェクト指向言語(ALGOL W)で最初の総合的な参照用型システムを設計していました。そのときの目標は、コンパイラによる自動チェックを用いて、あらゆる参照の利用を絶対的に安全にすることでした。しかし私はnull参照を導入する誘惑に勝てませんでした。実装があまりにも容易だったからです。そしてnull参照はおびただしい数のエラー、脆弱性、システムクラッシュを呼び込み、この40年間で数十億ドルに相当するつらみや損害を引き起こしました。
Null References: The Billion Dollar Mistakeからの原著者による引用文の大意

関連記事

Rails tips: Null Objectパターンでリファクタリング(翻訳)

Rubyのぼっち演算子はRailsの`Object#try`より高速(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。