なぜnullは10億ドルの誤りと呼ばれるか
ぬるぽの根っこは60年前の一つの設計判断にある。その来歴をたどれば、なぜOption型やKotlinのnull安全が生まれたか、不在を型で表す設計の勘所が腑に落ちる。
- Tony Hoareが1965年、ALGOL Wにnull参照を導入。理由は「実装が簡単だったから」。本人は2009年のQCon講演でこれを『10億ドルの誤り(billion-dollar mistake)』と公に悔いた。
- nullは型が「値あり」を約束していても実体は不在という抜け穴を作り、参照するとNullPointerException(Javaのぬるぽ)やセグメンテーション違反を引き起こす。検査漏れがコンパイル時に捕まらないのが本質的な害。
- 対策はML/HaskellのOption/Maybe型から、Kotlin・Swiftのnull安全、C#のnullable参照型、RustのOptionへ発展した。不在を型で表し、コンパイラに検査させる。
結論:不在を「値のふり」で表した一手が、全ての参照を疑わせた
横にスクロール
null参照の害は一言でいえば、「型が『ここに値がある』と約束しているのに、実体は不在でありうる」という嘘を言語が公認したことにある。ある変数の型が「文字列」なら、そこには常に文字列があるはずだ——この素朴な期待をnullは裏切る。結果、あらゆる参照に対して「本当に値があるか?」の検査が必要になり、その検査を書き忘れてもコンパイラは何も言わない。設計者Tony Hoare自身が後年これを「10億ドルの誤り」と呼んだのは、比喩ではなく、数十年分のクラッシュ・脆弱性・修正コストの総体を指した言葉である。
当時の状況と競合:1965年、ALGOL Wという実験場
1960年代半ば、Hoareはニクラウス・ヴィルト(Niklaus Wirth)とともにALGOL 60の後継言語 ALGOL W を設計していた。当時の新機軸はレコード(構造体)とそれを指すポインタ、すなわち参照の導入である。ここで生じたのが「まだ何も指していない参照」をどう表すかという問題だった。
Hoareが目指していたのは高い安全性だった。彼の理想は、コンパイラの型検査によってすべての参照の利用が絶対に安全であると保証することにあった。しかし「未初期化の参照」「連結リストの終端」のような不在状態を型の中でどう扱うかは、当時の型システムの語彙では自明ではなかった。選択肢は概念上ふたつあった。
| 方針 | 不在の表し方 | 検査のタイミング | 1965年当時の実装難度 |
|---|---|---|---|
| null参照 | どの参照型にも紛れ込める特別値 null | 実行時(参照した瞬間に破綻) | 極めて容易 |
| 不在を型で分離 | 「値あり/なし」を別の型で包む(後のOption型) | コンパイル時に検査を強制 | 当時は理論・実装とも未成熟 |
決定打・経緯:「実装が簡単だったから」という一言
勝敗を分けたのは、思想ではなく手軽さだった。Hoareは2009年、ロンドンのQConでの講演「Null References: The Billion Dollar Mistake」で、その判断を率直に振り返っている。要点は、すべての参照を安全にするという目標を掲げながら、null参照という抜け穴を入れた理由が「実装がとても簡単だったから(simply because it was so easy to implement)」だった、というものだ。
nullは、あらゆる参照型に代入でき、どんなポインタのビット表現にも「ゼロ」という空席を一つ用意するだけでよい。型ごとに「不在」を表現する専用の仕組みを設計・実装する労力に比べ、圧倒的に安上がりだった。この手軽さゆえにnullはALGOL Wから後続言語へ雪だるま式に受け継がれ、C・C++のヌルポインタ、JavaのnullとNullPointerException(日本の開発者が「ぬるぽ」と呼ぶもの)、多くの言語の標準装備となった。
被害の性質は一貫している。user.getName() のような呼び出しで user がnullだと、Javaなら NullPointerException、C/C++なら未定義動作やセグメンテーション違反になる。厄介なのは、この誤りが型の上では完全に正しく見える点だ。user の型はきちんと「ユーザー」であり、コンパイラは通す。破綻は実行時、しばしば本番環境で初めて露見する。
null参照は「検査を書き忘れても気づけない」性質を持つ。呼び出し側が毎回 null チェックを入れる規律に安全性を委ねるため、抜けは必ず生じ、しかもその抜けはコンパイル時に一切現れない。大規模コードでは「どの参照がnullになりうるか」が型に書かれておらず、人間の記憶と慣習だけが頼りになる。これが脆弱性やクラッシュを量産してきた。
今への影響と教訓:不在を型で表す設計へ
Hoareが本来望んだ「不在を型で分離する」道は、後の言語で本流になった。系譜はおおむね次の通りである。
- ML/Haskell(1970〜90年代):
Option(MLではoption)/Maybe型が登場。値はSome x/Just x(あり)かNone/Nothing(なし)のいずれか。「なし」を扱わないコードはコンパイルが通らず、検査漏れが型レベルで防がれる。 - Kotlin/Swift(2010年代):型そのものに「null許容」を刻む。Kotlinでは
Stringはnull不可、String?のみnull可。null可の値は安全呼び出し?.や非null表明!!を通さねば使えず、null安全(null safety)がコンパイラに強制される。SwiftのOptional(String?/if letによるアンラップ)も同思想だ。 - C#:後付けで nullable 参照型を導入し、
string(非null想定)とstring?を区別してnull由来の警告を出せるようにした。 - Rust:言語からnullを排除した。不在は標準ライブラリの
Option<T>(Some/None)で表し、参照は常に有効。ヌルポインタ参照というクラス自体を型システムで消し去った点で、Hoareの理想に最も近い。
これらに共通する原則は明確だ。「不在という状態を、値のふりをさせず、型として第一級で表現する」。そうすればコンパイラが「不在の可能性を処理したか」を検査でき、null参照の害の本質——検査漏れが実行時まで隠れること——が根本から絶たれる。
値が「無いかもしれない」なら、それを別の値(マジックナンバーやnull)で兼ねさせず、型で明示せよ。API設計でも、戻り値がOption/nullableであることをシグネチャに書けば、呼び出し側は不在の処理を忘れられなくなる。型は仕様書であり、コンパイラはその最も厳格な読者である。
型システムがなぜこうした保証を与えられるのかはプログラミングの型に関する解説を、堅牢なソフトウェアを支える基盤の考え方は開発運用(DevOps)を参照するとよい。
- null参照の考案者はTony Hoare、導入言語はALGOL W(1965年、ヴィルトと共同設計)
- 「10億ドルの誤り」は2009年QCon講演での本人の弁。導入理由は「実装が簡単だったから」
- 本質的な害は「不在の可能性が型に現れず、検査漏れが実行時(ぬるぽ/NPE)まで露見しない」こと
- 対策の系譜:ML/HaskellのOption/Maybe → Kotlin/Swiftのnull安全(?・!!)→ C#のnullable参照型 → nullを排したRustのOption
一段で言うと
nullは、不在という状態を「値のふり」で安く済ませた一手だった。手軽さと引き換えに、全ての参照へ「本当に値はあるか」という疑いを持ち込み、その検査漏れをコンパイラから隠した。だからHoareはそれを10億ドルの誤りと呼び、後の言語は不在を型で表すことでこの誤りを設計から締め出していった。教訓は普遍的だ——曖昧な状態は、別の値で兼ねず、型で語らせよ。
なぜ?の記事ガイド
なぜnullは10億ドルの誤りと呼ばれるかを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
プログラミング言語
比較で見る軸
難易度: advanced / カテゴリ: なぜ? / タグ数: 5
導入後に効く点
nullは型が「値あり」を約束していても実体は不在という抜け穴を作り、参照するとNullPointerException(Javaのぬるぽ)やセグメンテーション違反を引き起こす。検査漏れがコンパイル時に捕まらないのが本質的な害。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- なぜ?
- タグ数
- 5
判断チェックリスト
- 自社の用途が「プログラミング言語 / 型システム」に近いか確認する。
- 強みである「Tony Hoareが1965年、ALGOL Wにnull参照を導入。理由は「実装が簡単だったから」。本人は2009年のQCon講演でこれを『10億ドルの誤り(billion-dollar mistake)』と公に悔いた。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。