エラーの伝搬 (propagating errors) とは
Rust でエラーを返す可能性のある関数(Result
を返す関数)を呼び出すとき、その戻り値を受けて直ちにエラー処理を行うことは少なく、多くの場合は呼び出し元にエラーを返す(Error
値を伝搬させる)ことになると思います。
let book = match get_book(123) {
Ok(book) => book,
Err(err) => return Err(err.into()), // 呼び出し元にエラーを伝搬
};
このようなケースで、毎回 match
を使った条件分岐コードを記述していると、冗長なコードで溢れかえってしまいます。
そこで、Rust はエラーを呼び出し元に伝搬させるための ?
演算子を用意しています(Rust 1.13 以降)。
? 演算子の使い方
let book = get_book(123)?;
このように記述すると、get_book()
関数の戻り値が Result::Ok
だった場合はそれを unwrap()
した値が book
変数に格納され、戻り値が Result::Err
だった場合はそのエラーをそのまま return してくれます。
つまり、前述の match
を使ったコードと同じ振る舞いを 1 行で表現することができます。
?
演算子は、Option
を戻り値として返す関数の中でも使用することができます。
その場合、エラー発生時は Option::None
を返すという振る舞いになります。
main 関数での具体的な使用例
main()
関数の戻り値の型が、Result<(), Box<dyn std::error::Error>>
になっているのを見たことがあるかもしれません。
Box<dyn std::error::Error>
という型は、Error
トレイトを実装したあらゆる型を保持することができる Box
型です。
このような表現にしておくことで、その関数はあらゆる Error
型を返す可能性があることを示すことができます。
例えば、下記の main()
関数は戻り値として汎用的な Result
型を返すように定義しています。
この関数の中では、どんなエラーを返す関数呼び出しでも ?
演算子を使うことができます。
main()
関数の中から、明示的に独自のエラーを返すこともできます。