コメントで何度も述べたように、戻り値をオーバーロードする代わりに、本当にやりたいことは例外をスローすることです。戻り値に成功した値とエラー コードを混在させると、関数の使用が複雑になり、値の取得とエラーの検出の両方が複雑になり、バグが発生します。CPAN には、例外のスローとキャッチを容易にするモジュールがいくつかあります。 Try::Tiny、TryCatch、Throwable、およびException::Classが例です。
エラー コードの使用には、魅惑的で誤った経済性があります。これら 2 つのことは同等であると想定されます。
# Basic error handling with a return value and flag
my $value = function or die $Some::Module::error;
...continue...
# "Basic" error handling with exceptions
try {
my $value = function;
...continue...
}
catch {
die $@;
}
例外は非常に多くのコードのように見えます! その誤った同等性を除いて。あなたがやろうとしているのがエラーで死ぬことだけである場合、そしてほとんどの場合、これらは実際の同等の戻り値と例外のサンプルです。
# Basic error handling with a return value and flag
my $value = function or die $Some::Module::error;
...continue...
# Basic error handling with exceptions, for reals
my $value = function;
エラー フラグを返す場合、ユーザーは、すぐに死ぬことしかできない場合でも、常にエラーをチェックする必要があります。例外を使用する場合、ユーザーは何か特別なことをしたい場合にのみエラーをチェックする必要があります。標準の例外である何か、とあなたは言うかもしれません。
そして、それが例外の優れた経済性と安全性です。ユーザーの作業が少なくなり、誤って見逃すことはありません。
これで、実際に例外を使用したいということになりました。ここでは、必要なことを行うためのいくつかの方法を示します。これは、完全を期すためであり、それぞれがどこに該当するかを説明するためでもあります。これは、コンピューター API の古い、古い、古い問題であり、長い間例外が存在していたため、それを解決するための「賢い」方法がたくさんあります。いずれも例外に比べて深刻な問題を多く抱えています。見逃してしまうこともあるとは思いますが、これはウサギの穴をどれだけ深く掘り下げることができるかを説明するためのものです。
1) false を返し、グローバル変数にエラー コードを設定します。
これは、Perl コミュニティが例外を受け入れる前に、DBI などの多くのライブラリ (ところで、代わりに例外を使用することをお勧めします) でかなり一般的です。
my $dbh = DBI->connect($data_source, $username, $password)
or die $DBI::errstr;
Perl がそれを行う方法でもあります。
open my $fh, $file or die "Cannot open $file for reading: $!";
これには、エラー コードを返すという普遍的な問題があります。ユーザーがエラーをチェックするのを忘れた場合、プログラムはどこか他の場所でのぞき見が失敗することなく実行されます。例外は大声で失敗します。例外のチェックを忘れても、少なくとも問題の原因はわかっています。
最大の過ち (初心者も経験者も) の 1 つは、エラーのチェックを忘れることです。これが、 autodieのようなモジュールが非常に優れている理由です。
2 番目の問題は、すぐにエラー フラグを確認した場合にのみ機能することです。フラグはグローバル変数であり、別のエラーが発生すると、あなたのエラーが吹き飛ばされます。「まあ、それは問題ない、いつもだ」と思うかもしれませんfunction() or die $error_flag
。そうでもなければ...
sub function {
...blah blah blah...
if( $error ) {
$Error = "Something went wrong, oh god eject!";
another_function();
return 0;
}
else {
return $value;
}
}
another_function
エラーもある場合はどうなりますか?ライブラリの内部では、常にエラー フラグを設定し、すぐに戻るように注意する必要があります。「注意する」ことを含むシステムは、失敗する運命にあります。
$!
この問題は Perl で悩まされています。多くのことがそれを設定でき、Perl は複数の内部関数を呼び出し、単一の Perl 関数呼び出しで設定およびリセットすることがあります。
2) 単純な成功コードと複雑なエラー コードを返します。
これがシェルのやり方です。0 は成功、それ以外はすべてエラー コードです。
my $exit = system @args;
if( $exit != 0 ) {
...error handling...
...and what did error code 136 mean again?...
}
問題が明らかであることを願っています。興味深い値を返すことはできません。また、混乱を招き、エラーが発生しやすい自然な true == 成功、false == 失敗の規則を逆にします。も参照してくださいsystem
。
undef
また、成功で返される原因となる本当に悪いエラーを混同するのも簡単です。
3) 正の値は成功、負の値は失敗です。
これは 2 ですが、数値である限り、興味深い値を返すことができるようになりました。また、すべてのエラー値は数値でなければならず、それをどこかのテーブルで検索する必要があります。喜び。
my $return = function @args;
if( $return <= 0 ) {
...error handling...
...and what did error code -136 mean again?...
}
少なくとも true は「成功」と一致し、0 と undef の両方が失敗です。それは何かです。しかし、負の数も真です。ユーザーは間違いを犯す可能性が非常に高く、真の値を確認するだけで、エラー コードを見逃す可能性があります。
4) false であるオーバーロードされたオブジェクトを返します。
これは、例外なく得られる最高のものです。
すべての真の値は成功を示します。すべての false 値はエラーを示します。エラーは、ブール値が常に false になるようにオーバーロードされ、文字列値がエラー コードを返すようにオーバーロードされたオブジェクトを返すことによって示されます。
use overload
'""' => sub { $_[0]->error_string },
'bool' => sub { 0 },
'0+' => sub { 0 },
fallback => 1
;
true は成功、false はエラーです。エラーは互いに上書きできず、豊富で興味深いエラー情報を得ることができます。それは完璧ではありません。問題の 1 つは、false である成功した値を持つことができず、返すことができるものを制限することですが、それはかなり良いことです。
ただし、戻り値が必要なのでor die
イディオムは使えません。
my $value = function;
die $value if !$value;
エラーコードを返すという普遍的な問題が依然として残っており、ユーザーがチェックする必要があります。これは、エラー処理システムが持つ最大の問題であり、唯一の例外が解決します。
5) 戻り値の参照を渡します。
これを含めて、これでどこまで行けるかを示します。関数に値を返す代わりに、エラー コードを返します。どのように値を返しますか? 関数が値で設定する参照を渡します! クレイジーに聞こえますか?!
my $success_flag = open my $fh, $file;
おー。
これは、例外をサポートしていない多くの言語で推奨されるスタイルです。
厄介で連鎖不可能なインターフェースを犠牲にして、戻り値に干渉することなくエラーがあるかどうかを知ることができますが、エラーに関する情報を返すことはできません。エラー コードまたは文字列を返すには、true/false の成功/失敗を反転する必要があります (したがって、false は成功です)。
値を返し、代わりに参照を渡してエラーをキャッチすることは理にかなっていますが、相対的な意味でのみです。
my $fh = open \$error, $file or die "Can't open $file: $error";
これは私が野生で見たことはありませんが、かなりうまく機能します。エラー コードを返すことの最大の問題が、ユーザーがエラー コードを無視することである場合、最初の引数としてエラー リファレンスを強制的に渡すようにすることは、ユーザーにそれを押し付ける良い方法です。