今日の内容
- Result
はじめに
前回はOptionについて学びました。
今回は、Result について学びます。
Resultとは
Result<T, E>
は、Rustにおけるエラーハンドリングのための列挙型です。処理の結果が「Ok」か「Err」かを表現します。
- Ok(T): 成功した場合に返す値を格納する
- Err(E): 失敗した場合に返すエラーを格納する
これにより、Rustは例外(try-catch)の代わりに、コンパイル時にエラーを明確に扱うことができます。
Resultの定義
Result型は次のように定義されています。
標準ライブラリに用意されています。
enum Result<T, E> {
Ok(T),
Err(E),
}
- T: 成功時の値の型
- E: エラー時の情報の型
Resultの基本的な使い方
1.基本例
次の例では、整数の割り算を行い、ゼロ除算が発生した場合にエラーメッセージを返す関数を定義しています。
fn divide(a:i32, b:i32) -> Result<i32, String> {
if b == 0 {
Err("cannot divide by zero!".to_string())
} else {
Ok(a / b)
}
}
fn main() {
match divide(10, 2) {
Ok(result) => println!("{} / {} = {}", 10, 2, result),
Err(e) => println!("{}", e),
}
match divide(10, 0) {
Ok(result) => println!("{} / {} = {}", 10, 0, result),
Err(e) => println!("{}", e),
}
}
/******** 実行結果 ********
10 / 2 = 5
cannot divide by zero!
*************************/
2.Resultの値を取り出す方法
- match構文で取り出す
Optionと同様、match構文を使って値を取り出せますfn main() { let result: Result<i32, &str> = Ok(42); match result { Ok(value) => println!("成功: {}", value), Err(err) => println!("エラー: {}", err), } } /******** 実行結果 ******** 成功: 42 *************************/
- unwrap
unwrapはErrの場合にプログラムをクラッシュさせます
安全性を損なうため、基本的には避けるべきですfn main() { let result: Result<i32, &str> = Ok(42); println!("{}", result.unwrap()); } /******** 実行結果 ******** 42 *************************/
- unwrap_or
Errの場合に指定したデフォルト値を返しますfn main() { let result: Result<i32, &str> = Err("エラー"); println!("{}", result.unwrap_or(0)); } /******** 実行結果 ******** 0 *************************/
- unwrap_or_else
クロージャを使ってデフォルト値を計算できますfn main() { let result: Result<i32, &str> = Err("エラー"); let value = result.unwrap_or_else(|e| { println!("エラー内容: {}", e); -1 // デフォルト値 }); println!("{}", value); } /******** 実行結果 ******** エラー内容: エラー -1 *************************/
よく使う便利なメソッド
- is_okとis_err
ResultがOkかErrかを判定しますlet success: Result<i32, &str> = Ok(42); let failure: Result<i32, &str> = Err("エラー"); println!("{}", success.is_ok());//true println!("{}", failure.is_err());//true
- map
Okの値を変換しますlet result: Result<i32, &str> = Ok(10); let doubled = result.map(|x| x * 2); println!("{:?}", doubled);//Ok(20)
- and_then
Okの場合にさらに別のResultを返す処理を行いますfn to_even(x: i32) -> Result<i32, &'static str>{ if x % 2 == 0 { Ok(x) } else { Err("奇数です") } } fn main() { let result = Ok(4).and_then(to_even); println!("{:?}", result); let result = Ok(3).and_then(to_even); println!("{:?}", result); } /******** 実行結果 ******** Ok(4) Err("奇数です") *************************/
- or_else
Errの場合に別の処理を実行しますfn main() { let result:Result<i32, &str> = Err("エラー"); let recovered: Result<i32, &str> = result.or_else(|e| { println!("復帰処理: {}", e); Ok(0) }); println!("{:?}", recovered); } /******** 実行結果 ******** 復帰処理: エラー Ok(0) *************************/
- ?
Resultを扱う際の簡略記法
Okなら値を返し、Errなら関数を早期終了しますfn read_file(file_path: &str) -> Result<String, std::io::Error> { let content = std::fs::read_to_string(file_path)?; Ok(content) } fn main() { let content = read_file("test.txt"); println!("{}", content.unwrap()); }
OptionとResultの違い
前回学んだOptionとResultは似ていますが、異なる機能です。以下の表に示しました。
特徴 | Option | Result |
---|---|---|
目的 | 値が「存在するか」「しないか」を表現 | 成功かエラーかを表現 |
列挙子 | Some/None | Ok/Err |
主な用途 | 任意の値 | エラーハンドリング |
おわりに
今回はResultについて学びました。
次回は、OptionとResultを組み合わせたエラーハンドリングの応用について学んでいきます。
ご精読、ありがとうございました。