RustのエラーハンドリングはJavaなどの言語とは異なり、try...catch
のような仕組みは存在しません。一般的な方法は、アプリケーションレベルでグローバルなエラータイプを定義することです:
ここではthiserror
クレートを使用しており、独自のカスタムエラータイプを簡単に定義でき、コードを簡素化できます。簡潔に記述するために、AppResult
という型エイリアスも定義しています。
Rustのエラーハンドリングエコシステムでは、thiserror
とanyhow
という2つの主要なクレートがよく使用されます:
thiserror: ライブラリ開発者向けで、明確なエラータイプを定義するために使用されます。派生マクロを通じてstd::error::Error
トレイトを実装し、エラーの表示方法を定義できます。ライブラリを構築する場合や、ユーザーに明確なエラータイプを提供する必要がある場合に適しています。
anyhow: アプリケーション開発者向けで、汎用的なエラータイプanyhow::Error
を提供します。std::error::Error
トレイトを実装したあらゆるエラーを含むことができ、エラーの定義よりも伝播に重点を置いています。アプリケーション層のコードで様々なエラーをanyhow::Error
に簡単に変換でき、ボイラープレートコードの記述を減らせます。
場合によっては、これら2つのクレートを併用することもあります:ライブラリではthiserror
でエラータイプを定義し、アプリケーションではanyhow
でそれらのエラーを処理・伝播します。
Salvoでは、Handler
も様々なエラー(データベース接続エラー、ファイルアクセスエラー、ネットワーク接続エラーなど)に遭遇することがよくあります。このようなエラーに対しては、前述の手法を適用できます:
ここでhome
ハンドラはAppResult<()>
を直接返しています。しかし、このエラーをどのように表示すべきでしょうか? AppResult
というカスタムエラータイプに対してWriter
トレイトを実装する必要があり、この実装でエラーの表示方法を決定できます:
SalvoのHandler
はResult
を返すことができ、Result
内のOk
とErr
の型がどちらもWriter
トレイトを実装している必要があります。
anyhowの使用が広く普及していることを考慮し、Salvoはanyhow::Error
に対する組み込みサポートを提供しています。anyhow
機能を有効にすると、anyhow::Error
はWriter
トレイトを実装し、InternalServerError
にマッピングされます:
anyhow機能を使用するには、Cargo.tomlでSalvoのanyhow
フィーチャーを有効にする必要があります:
これにより、ハンドラ関数は直接anyhow::Result<T>
を返せるようになります:
エラーにはしばしば機密情報が含まれており、通常は一般ユーザーに見せたくないものです。しかし、開発者やサイト管理者であれば、エラーがすべての詳細を赤裸々に表示してくれることを望むかもしれません。
write
メソッドではRequest
とDepot
の参照を取得できるため、このような柔軟な処理を簡単に実装できます:
Salvoに組み込まれているエラーページはほとんどの場合に十分ですが、場合によってはエラーページの表示をカスタマイズしたいこともあります。
これはカスタムCatcher
を実装することで可能です。詳細についてはCatcher
のセクションを参照してください。