例外代わりの `Result<T, E>`
Result<T, E>
例外代わりの Rust には Result<T, E>
という型があります。だいたいこういう定義です。
enum Result<T, E> {
Ok(T),
Err(E),
}
Result<T, E>
は定義から分かるとおり、結果 (成否) を表す型で、 Ok
のときの型 T
と Err
のときの型 E
のどちらか一方を保持します。
Rust は他のプログラミング言語にあるような例外がありません。代わりにエラーハンドリングにはいくつかの方法がありますが、主に Result<T, E>
型を使用します。
Result<T, E>
は標準ライブラリを含めて広く使われている型です。 Option<T>
と同様に、 Rust をはじめるとすぐにふれることになる型のひとつです。
ここまで Option<T>
のときとほとんど同じ書き出しにしてみました。
基本的な使い方
Result<T, E>
は、たとえば次のように構築できます。
let r1: Result<i32, &str> = Err("Error!");
let r2: Result<i32, &str> = Ok(123);
T
と E
は任意の型を取れるので、↑の例のように E
として &str
を取ることもできますし、 ()
のように実質エラーの詳細を持たないこともできます。
let r1: Result<bool, ()> = Err(());
let r2: Result<bool, ()> = Ok(true);
Error
trait を実装した型を持つことが多いです。 Error
についてはまた別で書きます (たぶん) 。
Result<T, E>
は、たとえば次のように分解できます。
let r: Result<i32, &str> = Ok(123);
match r {
Ok(t) => println!("{t}"),
Err(e) => println!("{e}"),
}
match
は「 if より match 」 でも触れています。
Result<T, E>
を bool
にするメソッド
is_err
/ is_err_and
/ is_ok
/ is_ok_and
Option<T>
の is_none
や is_some
などと同様に is_err
や is_ok
などで bool
に変換できます。
let r: Result<i32, &str> = Err("Error!");
assert!(r.is_err());
let r: Result<i32, &str> = Ok(123);
assert!(r.is_ok());
let r: Result<i32, &str> = Err("Error!");
assert!(r.is_err_and(|e| e == "Error!"));
let r: Result<i32, &str> = Ok(123);
assert!(r.is_ok_and(|t| t == 123));
Result<T, E>
を T
や E
にする (中身を取り出す) メソッド
expect
/ unwrap
Option<T>
の expect
や unwrap
などと同様に Result<T, E>
も expect
や unwrap
でも中身を取り出せます。
let r: Result<i32, &str> = Ok(123);
assert_eq!(r.unwrap(), 123);
let r: Result<i32, &str> = Err("Error!");
r.unwrap(); // panic
// called `Result::unwrap()` on an `Err` value: "Error!"
let r: Result<i32, &str> = Ok(123);
assert_eq!(r.expect("r is Ok"), 123);
let r: Result<i32, &str> = Err("Error!");
r.expect("r is Ok"); // panic
// r is Ok: "Error!"
expect_err
/ unwrap_err
Option<T>
は T
のみなので、これで終わりですが、 Result<T, E>
は Ok
と Err
の 2 つがあるので、そのための unwrap もあります。
let r: Result<i32, &str> = Ok(123);
r.unwrap_err(); // panic
// called `Result::unwrap_err()` on an `Ok` value: 123
let r: Result<i32, &str> = Err("Error!");
assert_eq!(r.unwrap_err(), "Error!");
let r: Result<i32, &str> = Ok(123);
r.expect_err("r is Err"); // panic
// r is Err: 123
let r: Result<i32, &str> = Err("Error!");
assert_eq!(r.expect_err("r is Err"), "Error!");
unwrap_or
/ unwrap_or_default
/ unwrap_or_else
unwrap_or
は Err
の場合の値を指定することで unwrap します。 unwrap_or_default
は Err
の場合の値として T
の Default::default
の値を使用します。 unwrap_or_else
は Err
の場合の値の代わりに値を返す関数を指定します。
let r: Result<i32, &str> = Ok(123);
assert_eq!(r.unwrap_or(456), 123);
let r: Result<i32, &str> = Err("Error!");
assert_eq!(r.unwrap_or(456), 456);
let r: Result<i32, &str> = Ok(123);
assert_eq!(r.unwrap_or_default(), 123);
let r: Result<i32, &str> = Err("Error!");
assert_eq!(r.unwrap_or_default(), 0);
let r: Result<i32, &str> = Ok(123);
assert_eq!(r.unwrap_or_else(|_| 456), 123);
let r: Result<i32, &str> = Err("Error!");
assert_eq!(r.unwrap_or_else(|_| 456), 456);
ドクターメイト株式会社での運用
Option<T>
のときにも触れたのですが、ドクターメイト株式会社では unwrap
を使用しないようにしています。テストコードでも、です。理由は、テストコードで unwrap
を使用していると、プロダクションコード側で誤って unwrap
の使用を残してしまっていても検索でそれに気づくことが難しくなるためです。安全に unwrap できる場合は代わりに expect
を使用しています。
一方で unwrap_err
は意外とテストにおけるエラーの検証に使われています。 unwrap
はダメだけど、 unwrap_err
はいいんですね。ふしぎですね。
次回予告
ほとんど Option<T>
と同じですね! 次回もほとんど Option<T>
と同じになりそうですね!! ?
演算子に触れると思います。たぶん。
Discussion