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の基礎文法はこれで完了. 次の章からは,学んだ知識を使って四則演算の電卓を作っていく.