他の言語でプログラミングをしているとき、NullPointerException(いわゆる「ぬるぽ」)に悩まされたことはありませんか? Rustには、そもそも「Null」という概念が存在しません。その代わりに、値があるかないかを安全に扱うOption型と、失敗する可能性を管理するResult型が用意されています。
今回は、Rustの安全性と便利さを支える「エラー処理」と、データをまとめて扱う「ベクタ(Vec)」や「文字列(String/&str)」の正体を解き明かしていきましょう!
RustにはNullがありません。その代わりに、「値があるかもしれないし、ないかもしれない」という状態をOption型というEnumで表現します。
let some_number = Some(5);
let no_number: Option<i32> = None;中身を無理やり取り出すには .unwrap() を使いますが、もし中身が None だった場合、プログラムが強制終了(パニック)してしまいます。
let x: Option<i32> = None;
// let value = x.unwrap(); // ここでパニック!「ぬるぽ」と同じ恐怖安全な取り出し方(match式) 前回の記事で学んだ match を使うのが最も確実です。
match x {
Some(i) => println!("値は{}です", i),
None => println!("値はありませんでした"),
}次に、処理が「成功したか失敗したか」を表すResult型です。ファイルの読み込みや計算など、エラーが起こりうる場面で多用されます。
Rustには、エラーが発生した瞬間にその関数を終了してエラーを返す「?演算子」という非常に便利な仕組みがあります。
// イメージ:ファイルの読み込みなどが失敗したら即座にErrを返す
let result = perform_action()?; 同じ型のデータをたくさん並べて管理したいときに使うのがベクタ(Vec)です。 配列と似ていますが、ベクタは「後から要素を増やしたり減らしたりできる」のが最大の特徴です。
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
// マクロを使って簡単に作成も可能
let v2 = vec![10, 20, 30];
// 要素を取り出す(Option型が返ってくる!)
let second = v.get(1); // Some(&2) が返るここでポイントなのは、v.get() の戻り値が Option 型であることです。インデックス範囲外を指定しても、エラーで落ちるのではなく None が返ってくるため、安全に処理が書けます。
初心者が最も混乱するのが「文字列」です。Rustには主に2種類の文字列があります。
let mut s = String::from("Hello"); // String型
s.push_str(", world!"); // 変更可能
let slice: &str = &s; // &str型(参照)
println!("{}", slice);なぜ2つあるのか? それは、Rustの核心である「メモリ管理」を効率的に行うためです。すべてを String にするとメモリ消費が激しくなり、すべてを &str にすると柔軟性がなくなります。用途に応じて使い分けるのがRust流です。
None を扱うことで「ぬるぽ」を撲滅!これらをマスターすれば、Rustで実用的なプログラムを書く準備が整います。 特に String と &str の使い分けは、最初は戸惑いますが、書き続けるうちに「メモリをどう扱っているか」が意識できるようになり、楽しくなってくるはずです!