エラーハンドリングには様々な方法があります。

JavaScript と Python は例外を投げるが、Rust 言語は間接的に例外を投げる。
C 言語と Go 言語ではエラーコードを返します。その値が -1 または nil かどうかを判断する必要があります。
どの方法が良いのか、ずっと気になっていました。
ついこないだ、私は長いこと読んでいた一つの記事を読みました記事明確に主張する例外を投げる方が状態コードを返すより良い彼の理由は説得力があり、記事がまだ中国語訳されていなかっただけで、私が翻訳した。
エラーと返却ステータスコード
著者:ネルド・バッチェルド(Ned Batchelder)
原文のURL:nedbatchelder.com

ソフトウェアにおいて、エラーハンドリングには二つの方法があります:例外を投げる(throwing exceptions)と状態コードを返す(returning status codes)。
ほとんどの人は例外の方が良い処理方法だと考えますが、まだ状態コードを好む人もいます。本文では、なぜ例外がより良い選択なのかを説明します。
一、コードのクリーンさ
例外を使うと、大部分のコードでエラーハンドリングのステップを省略できます。例外は捕捉されない層を通じて自動的に上に伝播されます。そのため、エラーハンドリングのロジックを一切含まないコードを書くことができ、コードをシンプルで読みやすく保つのに役立ちます。
同じ簡単な関数を二つの方法で書いてみましょう。
まずは状態コードを返す方法です。
STATUS DoSomething(int a, int b) { STATUS st; st = DoThing1(a); if (st != SGOOD) return st; st = DoThing2(b); if (st != SGOOD) return st; return SGOOD; }
上記の例では、DoThing1(a)とDoThing2(b)の戻り値が正常かどうかを確認して、次のステップに進めるか。
例外がスローされた場合、中間のエラーチェックは不要である。
void DoSomething(int a, int b) { DoThing1(a); DoThing2(b); }
これは最も簡単なケースであり、複雑なシナリオに遭遇した場合、状態コードによるノイズはさらに深刻になる。例外はコードの整理を維持できる。
二、意味のある戻り値
状態コードは貴重な戻り値を占有するため、戻り値が正しいかどうかを判断するためのコードを追加しなければならない。
一部の関数は本来正常値を返すだけであるが、エラーの状況を返す必要がある。時間が経つにつれて、コード量は増加し続け、関数は大きくなり、戻り値も複雑になる。
例えば、多くの関数の戻り値はオーバーロードされている:"失敗した場合、NULLを返す"または"失敗した場合、-1を返す"。結果として、このメソッドを呼び出すたびに、戻り値がNULLまたは-1かどうかを確認する必要がある。関数が後に新しいエラーレター値を追加した場合、すべての呼び出しポイントを更新する必要がある。
例外がスローされる場合、関数は常に成功した場合にのみ返り値を返し、すべてのエラーハンドリングも一箇所で簡略化できます。
三、より豊富なエラーメッセージ
状態コードは通常整数であり、伝達できる情報は非常に限られています。エラーがファイルが見つからない場合、どのファイルでしょうか?状態コードではそれほど多くの情報を伝達できません。
状態コードを返す際には、専用のエラーログにエラーメッセージを記録するのが良いでしょう。呼び出し元はそこから詳細な情報を取得できます。
例外は全く異なり、クラスのインスタンスであるため、大量の情報を持ち運べます。例外はサブクラス化できるため、異なる例外は異なるデータを持ち、非常に豊富なエラーメッセージ体系を形成できます。
四、暗黙のコードを扱える
一部の関数は状態コードを返すことができません。例えば、コンストラクタには明示的な返り値がないため、状態コードを返すことができません。また、デストラクタのような関数は直接呼び出すことさえできず、もっと言えば返り値すらありません。
値を返さない関数は、例外処理を使用しない場合、エラーメッセージを伝える他の方法を考えなければならず、またはその関数が失敗しないように装わなければならない。シンプルな関数は正常に動作するかもしれないが、コード量は増え続け、失敗する可能性も増える。失敗を表現する方法がない場合、システムはよりエラーが起きやすくなり、理解しにくくなる。
五、エラーの可視性
考慮してほしい。プログラマーが不注意でエラーハンドリングコードを書かなかった場合、どうなるか?
返される状態コードがチェックされない場合、エラーは発見されず、コードは成功したかのように実行を続ける。後でコードが失敗するかもしれないが、それは多くのステップの操作の後のことかもしれず、最初のエラーが発生した場所に問題をどのように遡る?
逆に、例外がすぐに捕捉されなかった場合、呼び出しスタックを上に伝播し、より高い catch ブロックに到達するか、トップレベルに到達してオペレーティングシステムが処理することになります。オペレーティングシステムは通常、エラーをユーザーに表示します。これはプログラムにとって良いことではありませんが、エラーは少なくとも見えるものです。例外を見ることができ、それが投げられた場所や捕捉されるべき場所を判断できるので、コードを修正できます。
ここでは、エラーが報告されない場合について議論しません。このような状況では、戻りステータスコードを返すか例外を投げても役立ちません。
したがって、報告されたエラーが処理されない場合、2つの状況に分けられます:1つは戻りステータスコードが問題を隠蔽し、もう1つは例外を投げ出すことでエラーが見えるというものです。どちらを選びますか?
六、反論
有名なプログラマー Joel Spolsky は、戻りステータスコードの方が良いと考え、例外はより悪い goto 文と考えるためです。
「例外はソースコードでは見えません。コードブロックを読んでいると、どのような例外がスローされる可能性があるか、どこでスローされるかを知ることはできません。これは、慎重にコードを検査しても、潜在的なエラーを見つけることができないことを意味します。」
「例外は関数に多くの可能性のある出口を作り出します。正しいコードを書くには、すべての可能性のあるコードパスを考慮する必要があります。例外をスローする可能性のある関数を呼び出すたびに、すぐに例外をキャッチしない場合、関数が突然終了するか、予想していなかった他のコードパスが発生する可能性があります。」
「これらの話は聞きにくいかもしれませんが、返り値の状態コードに変更すると、関数のすべての可能性のある返り点を明示的にチェックする必要があります。したがって、あなたは明示的な複雑さを隠示的な複雑さと引き換えました。これにも欠点があり、明示的な複雑さは木を見て森を見ないようにし、コードが乱雑になる原因になります。」
「このような明示的な複雑さに直面すると、プログラマーは煩わしく感じ、最終的にはカスタムメソッドでエラーハンドリングを隠したり、単純にエラーハンドリングを省略したりする可能性があります。」
最初のものはエラーハンドリングを隠蔽しており、明示的な処理を再び隠示的な処理に変えるだけであり、元のTryメソッドほど便利でも機能が豊富でもありません。後者はエラーハンドリングを省略する方がさらに悪く、プログラマーが特定のエラーが発生しないと仮定し、リスクを潜めてしまうのです。
七、まとめ
返り値コードは使いにくく、一部では全く使えません。返り値を乗っ取ります。プログラマーは容易にエラーハンドリングコードを書かず、システム内で静かな障害を引き起こすことがあります。
例外は返り値コードより優れています。あなたのプログラミング言語が例外処理ツールを提供していれば、それらを使ってください。
(終)


























