エラーハンドリング Result<T, E>
Rustのエラーハンドリングは以下の2種類にグループ分けされる
- 回復不可能なエラー
panic!
- 回復可能なエラー
Result<T, E>
Result<T, E>
Rustには例外処理が存在しないため、その代わりにResult<T, E>を使う。
pub enum Result<T, E> { Ok(T), Err(E), }
各variant
Ok(T)
成功時Err(E)
失敗時
match 式によるハンドリング
例1: ファイルを開く
use std::fs::File; fn main() { let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => panic!("faild to open file: {:?}", error), }; }
Note:
match
式を使って、成功したときは値を取得し、失敗時はpanic!マクロでプログラムを終了させている。
更に取り出したerror
を解析する場合
match式を取り替える
エラーハンドリングがどんどんネストしていくとmatch式もネストしていく。
use std::fs::File; use std::io::ErrorKind; fn main() { let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => match error.kind() { ErrorKind::NotFound => match File::create("hello.txt") { Ok(fc) => fc, Err(e) => panic!("failed to create the file: {:?}", e), }, _ => panic!("Problem opening the file: {:?}", error), } } }
Result<T, E>
に実装された unwrap_or_else
メソッドに置き換えることができる。
use std::fs::File; use std::io::ErrorKind; fn main () { let f = File::open("hello.txt").unwrap_or_else ( |error| { if error.kind() == Error::NotFound { File::create("hello.txt").unwrap_or_else( |error| { panic!("Problem creating the file: {:?}", error); }); } else { panic!("Problem opening the file: {:?}", error); } }); }
panic!のためのショートカット
以下の2つのメソッドを使うことでmatch式を使うことなく、エラー発生時、panic!を発生させることができる
unwrap()
メッセージなしexpect(message)
panic!マクロにメッセージを渡す
use std::fs::File; fn main() { let f = File::open("hello.txt").unwrap(); }
以下と同等
use std::fs::File; fn main() { let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => panic!("{}", error), } }
expect
メソッドは unwrap
と似ているが、 エラーメッセージを渡すことができるので、unwrap
よりも expect
を使う。
use std::fs::File; fn main() { let f = File::open("hello.txt").expect("Failed to open hello.txt"); }
エラーの伝搬
関数内部でエラーを処理するのではなく、呼び出し元に Result<T, E>
を返すことで、呼び出し側でエラーのハンドリングが行える。
例: ファイルの中身をStringに読み込む
use std::fs::File; use std::io; use std::io::Read; fn read_from_file() -> Result<String, io::Error> { let f = File::open("hello.txt"); let mut f = match f { Ok(file) => file, Err(e) => return Err(e), // Result<T, io::Error>を返す } let mut s = String::new(); match f.read_to_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), } }
エラーの伝搬のためのショートカット
?
演算子を使うことで、エラーの伝搬をショートカットすることができる
std::fs::File; std::io; std::io::Read; fn read_from_file() -> Result<String, io::Error> { let f = File::open("hello.txt")?; let s = String::new(); f.read_to_string(&mut s)?; Ok(s); }
?
演算子を Result
の後ろに置くことで、match
式と同様に、エラー発生時、Err型を自動で返してくれる。
Errの生成は、内部でFromトレイトを通して生成される。
また ?
演算子を使うことでボイラープレートを削除し、シンプルにする。
use std::fs::File; use std::io; use std::io::Read; fn read_from_file() -> Result<String, io::Error> { let mut s = String::new(); File::open("hello.txt")?.read_to_string(&mut s)?; Ok(s) }
参照
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html
https://doc.rust-lang.org/std/result/enum.Result.html
https://rust-lang-nursery.github.io/rust-cookbook/errors/handle.html