「失敗したが続行したい」——既存の4パターンでは表現しきれない状況

A Novel Look at Error Handling in Rustの筆者は、Rustのエラー処理を大きく4つに整理しています。関数が失敗しうるとき、開発者は次のいずれかを選ぶのが一般的だと述べています。

  • panicさせ、ドキュメントに失敗条件を明記する
  • Optionを返し、失敗時はNoneにする
  • Resultを返し、エラー内容を型で表現する
  • デフォルト値で復帰し、処理を止めずに続行する

この筆者が問題視するのは4つ目の「デフォルト値での復帰」です。処理は止まらない一方で、呼び出し元は内部で失敗が起きたことを知る手段がありません。(出典)

「失敗はしたが処理は続けたい」という状況は珍しくないのに、既存の型設計だけでは表現しきれていない。

記事では、コンパイラが複数のエラーをまとめて収集するケースや、ファイルパスが存在しない場合にデフォルトパスを使うケース、ゼロ除算を0として扱うケース、テストスイートで一部が失敗しても全件を実行し続けるケースなど、具体例が挙げられています。この続きとして筆者独自の解決策が提示されていますが、詳細は元記事で確認できます。

開発中は「後回し」にしたいという本音

Work In Progress Rustでは、少し違う角度からエラー処理の悩みが語られています。Rustのコンパイラ(rustc)は、Resultを返し忘れていたり、特定のエラーケースを処理していなかったりすると、その場でコンパイルを止めます。

複雑なロジックを書いている最中に限って、rustcが立ちはだかってくる。

筆者は、まず「うまくいく道筋(happy path)」を書き切ってから、エラー処理を後で整えられるようにするための独自ライブラリを開発したと述べています。(出典)なお、この記事は生成AIツールを使わずに書かれたと明記されている点も、開発者ブログらしい実直さを感じさせます。

普段はPython・TypeScriptを中心に書いている身からすると、コンパイラに強制されるからこそ後々のバグが減るというRustの安全性には魅力を感じます。一方で、探索的にコードを書きたい開発初期の段階では、このスピード感とのバランスを取るのが難しそうだとも感じます。

初心者もハマる .map() 内の ? 演算子

Rust - Handling Results In A Map Closureは、より実践的なつまずきを扱っています。Resultを返す関数を.map()クロージャの中で呼び出し、?演算子でエラーを伝播させようとすると、コンパイルエラーになるというものです。(出典)

理由は単純で、?演算子は「それを使っているクロージャ自体」がResultかOptionを返す必要があるためです。外側の関数がResultを返していても、クロージャ自体がそうでなければエラーになります。学習者がよく踏むパターンとして紹介されています。

3つの記事の着眼点

記事 着眼点 主な対象読者
A Novel Look at Error Handling in Rust 型システムで表現し切れない「失敗しても続行」パターンの設計論 言語設計・アーキテクチャに関心がある人
Work In Progress Rust 開発中の厳密なエラー処理を後回しにする実践テクニック 日常的にRustを書く実務者
Rust - Handling Results In A Map Closure .map()内で?を使う際の具体的な落とし穴 Rust学習中の人