blog.dopana

Back

null の考案者であるトニー・ホーアは、かつて自身の発明を「10億ドルの過ち」と呼びました。Nullポインタの参照外しは、数え切れないほどのシステムクラッシュや脆弱性を引き起こしてきたからです。Rustは Option<T> 列挙型と Some(T) ヴァリアントを採用することで、null のリスクをコンパイル時に完全に根絶しました。

10歳でもわかる説明:謎のプレゼント箱#

宅配便で届いた包装された箱をイメージしてください:

  1. Some(おもちゃ): 包装紙を開けると、中から本物のおもちゃが出てきます。
  2. None: 箱を開けたら完全に空っぽでした。
graph TD
    Box["Option&lt;T&gt; (宅配の箱)"] --> Check{"中に物が入っているか?"}
    Check -->|Yes: 存在| SomeBranch["Some(値)<br/>(型 T の実際のデータを保持)"]
    Check -->|No: 空っぽ| NoneBranch["None<br/>(値が存在しない状態)"]
    SomeBranch --> Unbox["match や if let で安全に箱を開封"]
    NoneBranch --> Fallback["クラッシュさせずに安全にフォールバック処理"]

他のプログラミング言語では、空の箱であるにもかかわらず中身があると思い込んで操作(user.getName() など)してしまい、実行時エラーを引き起こします。Rustでは明示的に箱を開封(アンパック)しない限り、Some(T) の内部データを使用することはできません。

Option<T> の定義#

Option<T> は標準ライブラリのPreludeに組み込まれているため、宣言なしで直接使用できます:

enum Option<T> {
    Some(T),
    None,
}
rust

Option<T> と型 T はコンパイル時に明確に区別されるため、Option<i8> を通常の i8 と誤認して演算することは不可能です:

let x: i8 = 5;
let y: Option<i8> = Some(5);

// let sum = x + y; // ❌ コンパイルエラー: Option<i8> と i8 は加算不可
rust

Some() を扱う4つの実践的パターン#

1. match 式による完全なパターン網羅#

2. if let による簡潔な取り出し#

Some の場合のみ処理を行い、None は無視したい場合:

src/main.rs
let config_timeout: Option<u64> = Some(3000);

if let Some(timeout) = config_timeout {
    println!("設定されたタイムアウト: {timeout}ms");
}
rust

3. デフォルト値の設定: unwrap_orunwrap_or_else#

本番コードでは単なる .unwrap() を避け、フォールバック値を指定します:

src/main.rs
let port: Option<u16> = None;

// 即値でデフォルト値を指定
let active_port = port.unwrap_or(8080);
println!("ポート: {active_port}"); // 8080

// クロージャで遅延評価してデフォルト値を生成
let env_port = port.unwrap_or_else(|| {
    4000 + 4000
});
println!("フォールバックポート: {env_port}");
rust

4. コンビネータによる関数型変換#

中身を取り出さずにチェーンメソッドでスマートに値を加工できます:

src/main.rs
let raw_input: Option<&str> = Some("   rustacean   ");

let cleaned = raw_input
    .map(|s| s.trim())
    .filter(|s| !s.is_empty())
    .map(|s| s.to_uppercase());

println!("{cleaned:?}"); // Some("RUSTACEAN")
rust
graph LR
    Input["Some('   rustacean   ')"] -->|map trim| Trimmed["Some('rustacean')"]
    Trimmed -->|filter not empty| Filtered["Some('rustacean')"]
    Filtered -->|map uppercase| Final["Some('RUSTACEAN')"]

他言語のNull表現とRustの比較#

言語表現方法安全性レベルチェック漏れ時の挙動
JavaScript / TypeScriptnull / undefined実行時 TypeError クラッシュ
Java / C#null ポインタ中〜低NullPointerException
PythonNoneAttributeError: 'NoneType'
RustOption<T> (Some / None)コンパイル時100%安全コンパイルエラーで未処理を検知

まとめ#

  • Some(値)Option<T> における有効な値の存在を表します。
  • None は値の非存在を表します。
  • Rustは nullOption<T> に置き換えることで、Nullポインタによる実行時クラッシュを根絶しました。
  • 網羅的チェックには match、単一パターンには if let、関数型チェーンには .map().unwrap_or() を活用します。

参考資料#