RustにはJavaやC++のような一般的な例外は存在せず、代わりにエラー返却のためにタイプラッパーResultとOptionが使用されます。
Result<T, E> — 値(Ok(T))またはエラー(Err(E))のいずれかを保持します。通常、正常に実行されない可能性のある操作(例えば、ファイルの入出力など)に使用されます。
Option<T> — オプショナルな値のために使用されます:Some(T)またはNoneで、エラーに関する情報はありません。例:
fn divide(x: f64, y: f64) -> Option<f64> { if y == 0.0 { None } else { Some(x / y) } }
Rustにはtry/catchスタイルの例外が存在しないため、エラーの透過的で管理可能な流れを確保し、例外を維持するためにガベージコレクタやアンワインドエンジンを必要としません。
なぜpanic!を通常のエラー処理に使用できないのか?
回答:panic!は実行フローを終了させ、回復の機会を与えず、アンワインド/中止を引き起こします。一方、エラーがResultを介して返される場合、呼び出し側で処理することができます。例:
fn foo() -> Result<(), String> { Err("何かがうまくいかなかった".to_string()) } // 代わりに fn foo() { panic!("何かがうまくいかなかった"); }
物語
画像処理のマイクロサービスでは、開発者がすべての異常な状況の処理に
panic!を使用していました。これにより、エラーが発生するたびにサービスが異常終了し、クライアントにエラーの詳細を含む適切なHTTPレスポンスを返すことができませんでした。
物語
あるCLIツールでは、すべての入出力に
unwrap()が使用され、可能なエラーの処理がされていませんでした。その結果、ファイルシステムのエラーが発生すると、プログラムは何のメッセージや診断もなく単に終了しました。
物語
分析サービスでは、わずかなエラーのためにOptionを使用しようとした結果、実際のエラー情報が失われ、デバッグが困難になりました。エラーの列挙体を持つResultに移行した後、バグを見つけて修正するのが容易になりました。