導入
多くの言語を悩ませてきたのが null(値がない状態)です。null を持つ変数をうっかりそのまま使い、実行時にクラッシュ——「10億ドルの過ち」とも呼ばれるこのバグを、Rust は Option 型で根本から防ぎます。Rust に null はありません。
説明
Option<T> は標準ライブラリの enum で、「値があるか・ないか」を表します。
enum Option<T> {
Some(T), // 値がある(中身は T 型)
None, // 値がない
}
「値がないかもしれない」ものは、すべてこの Option で包まれて返ってきます。たとえば配列から要素を探す find、割り算の安全版などです。
fn main() {
let numbers = [1, 2, 3];
// get は「範囲内なら Some(値)、範囲外なら None」を返す
let a: Option<&i32> = numbers.get(1); // Some(&2)
let b: Option<&i32> = numbers.get(10); // None
// 中身を使うには、None の場合も必ず考えさせられる
match a {
Some(v) => println!("値は {v}"),
None => println!("値がない"),
}
}
ここが決定的に重要です。numbers.get(1) は 2 を直接返すのではなく、Some(2) という箱に入れて返します。中身を使うには、match などで None の場合を必ず処理しなければなりません。「値があると思い込んでそのまま使う」ことが型のレベルで不可能なのです。だから null 由来のクラッシュが起きません。
毎回 match を書くのが冗長なときは、便利メソッドが使えます。
fn main() {
let x: Option<i32> = Some(5);
// if let: 1つのケースだけ処理したいとき
if let Some(v) = x {
println!("あった: {v}");
}
// unwrap_or: None のときの既定値を指定
let y: Option<i32> = None;
println!("{}", y.unwrap_or(0)); // None なので 0
// map: 中身があれば変換
let doubled = Some(5).map(|n| n * 2); // Some(10)
println!("{:?}", doubled);
}
if let Some(v) = x… 「Someのときだけ」を簡潔に書く構文。unwrap_or(既定値)…Noneのときの代わりの値を指定。map(...)… 中身があるときだけ関数を適用(NoneならそのままNone)。
Option は「値がないかもしれない」を、null のように隠さず、型として明示します。コンパイラが「ない場合の処理」を必ず書かせてくれるので、実行時の null 参照エラーとは無縁になります。次章では、この考え方をエラー処理に広げた Result を学びます。
試すには
Some(5) の中身を取り出そうと x + 1 と直接書くとエラーになります(Option<i32> と i32 は別の型)。必ず match・if let・unwrap_or などで「None の場合」を経由させられます。この「箱を必ず開けさせる」設計が、null 安全の正体です。