Option と Result
Option: あるかないか
Rust には null がない.代わりに Option を使う:
enum Option<T> {
Some(T), // 値がある
None, // 値がない
}
<T>は「何の型でも入る」という意味.Option<i32>なら「i32 があるかないか」,Option<String>なら「String があるかないか」.
fn find_even(numbers: Vec<i32>) -> Option<i32> {
for n in numbers {
if n % 2 == 0 {
return Some(n);
}
}
None
}
fn main() {
let nums = vec![1, 3, 4, 7];
let result = find_even(nums);
match result {
Some(n) => println!("found: {}", n),
None => println!("not found"),
}
}
Option の扱い方
unwrap: 「絶対ある!」
fn main() {
let x: Option<i32> = Some(42);
let value = x.unwrap(); // 42
println!("{}", value);
}
None に対して unwrap() するとプログラムが落ちる(パニック).「絶対に Some のはず」という確信があるときだけ使う.
expect: unwrap + メッセージ付き
fn main() {
let x: Option<i32> = Some(42);
let value = x.expect("値がないとおかしい");
println!("{}", value);
}
unwrap と同じだけど,落ちたときのメッセージを指定できる.
match: 安全に処理
fn main() {
let x: Option<i32> = Some(42);
match x {
Some(v) => println!("got: {}", v),
None => println!("nothing"),
}
}
if let: マッチが 1 パターンだけのとき
fn main() {
let x: Option<i32> = Some(42);
if let Some(v) = x {
println!("got: {}", v);
}
}
None のときは何もしない場合に便利.
Result: 成功か失敗か
Result は処理が成功したか失敗したかを表す:
enum Result<T, E> {
Ok(T), // 成功(値は T)
Err(E), // 失敗(エラーは E)
}
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("division by zero"))
} else {
Ok(a / b)
}
}
fn main() {
match divide(10.0, 3.0) {
Ok(result) => println!("result: {}", result),
Err(e) => println!("error: {}", e),
}
match divide(10.0, 0.0) {
Ok(result) => println!("result: {}", result),
Err(e) => println!("error: {}", e),
}
}
Result も unwrap できる
fn main() {
let result = divide(10.0, 3.0);
let value = result.unwrap(); // 3.333...
println!("{}", value);
}
Err に対して unwrap() するとパニック.Option と同じルール.
? 演算子: エラーを上に投げる
関数の中で ? を使うと,Err だった場合にそのまま呼び出し元に返してくれる:
fn calc(a: f64, b: f64, c: f64) -> Result<f64, String> {
let ab = divide(a, b)?; // Err なら即 return
let result = divide(ab, c)?;
Ok(result)
}
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("division by zero"))
} else {
Ok(a / b)
}
}
fn main() {
match calc(100.0, 5.0, 2.0) {
Ok(v) => println!("result: {}", v),
Err(e) => println!("error: {}", e),
}
}
? は「成功なら値を取り出す,失敗ならこの関数から即 Err を返す」という意味.戻り値が Result の関数でしか使えない.
よく見るパターン
標準ライブラリの多くの関数が Result を返す:
fn main() {
// 文字列 → 数値の変換(失敗するかもしれない)
let num: Result<i32, _> = "42".parse();
println!("{}", num.unwrap()); // 42
let bad: Result<i32, _> = "hello".parse();
println!("{}", bad.is_err()); // true
}
_は「型を推論に任せる」という意味.
まとめ
| 型 | 意味 | 中身 |
|---|---|---|
Option<T> |
あるかないか | Some(T) / None |
Result<T, E> |
成功か失敗か | Ok(T) / Err(E) |
扱い方:
| やり方 | いつ使う |
|---|---|
match |
安全に両方のケースを処理したいとき |
if let |
片方だけ処理すれば十分なとき |
unwrap() / expect() |
絶対に Some/Ok のはずというとき |
? |
エラーを呼び出し元に投げたいとき |
おめでとう.Rust の基礎文法はこれで完了. 次の章からは,学んだ知識を使って四則演算の電卓を作っていく.