209

Go言語の作成者は次のように書いています

Goはアサーションを提供しません。それらは紛れもなく便利ですが、私たちの経験では、プログラマーは適切なエラー処理とレポートについて考えることを避けるためにそれらを松葉杖として使用しています。適切なエラー処理とは、サーバーがクラッシュするのではなく、致命的でないエラーの後で動作を継続することを意味します。適切なエラー報告とは、エラーが直接的かつ的を射たものであることを意味し、プログラマーが大きなクラッシュトレースを解釈する手間を省きます。正確なエラーは、エラーを見たプログラマーがコードに精通していない場合に特に重要です。

これについてどう思いますか?

4

21 に答える 21

328

いいえ、assert意図したとおりに使用する限り問題はありません。

つまり、通常のエラー処理とは対照的に、デバッグ中に「起こり得ない」ケースをキャッチするためのものです。

  • アサート: プログラムのロジック自体の失敗。
  • エラー処理: プログラムのバグによるものではない、誤った入力またはシステム状態。
于 2009-12-06T04:20:46.577 に答える
111

いいえ、悪でgotoもありません。assertしかし、どちらも悪用される可能性があります。

Assert はサニティ チェック用です。正しくない場合にプログラムを強制終了する必要があるもの。検証用ではなく、エラー処理の代わりとしても使用できません。

于 2009-12-06T04:11:30.620 に答える
61

その論理によれば、ブレークポイントも悪です。

アサートはデバッグ支援として使用する必要があり、それ以外には使用しないでください。「悪」とは、エラー処理の代わりにそれらを使用しようとするときです。

アサーションは、プログラマーが存在してはならない問題を検出して修正し、仮定が正しいことを確認するのに役立ちます。

それらはエラー処理とは何の関係もありませんが、残念なことに、一部のプログラマーはそれらをそのように悪用し、「悪」と宣言します。

于 2009-12-06T10:48:42.777 に答える
41

assert をよく使います。初めてアプリケーションを作成するとき (おそらく新しいドメイン用) に非常に便利です。非常に手の込んだエラー チェック (時期尚早の最適化と見なす) を行う代わりに、高速にコーディングし、多くのアサートを追加します。物事がどのように機能するかについて詳しく知った後、書き直していくつかのアサートを削除し、エラー処理を改善するためにそれらを変更します。

アサーションのおかげで、プログラムのコーディング/デバッグに費やす時間が大幅に短縮されました。

また、アサーションは、プログラムを壊す可能性のある多くのことを考えるのに役立つことにも気付きました。

于 2009-12-06T04:16:25.477 に答える
31

追加情報として、go には組み込み関数が用意されていますpanic。これは の代わりに使用できますassert。例えば

if x < 0 {
    panic("x is less than 0");
}

panicはスタック トレースを出力するので、何らかの目的でassert.

于 2009-12-07T06:49:18.827 に答える
30

プログラムのバグを検出するために使用する必要があります。悪いユーザー入力ではありません。

正しく使えば悪ではありません。

于 2009-12-06T04:29:01.673 に答える
13

これはよく出てきますが、アサーションの防御を混乱させる問題の 1 つは、アサーションがしばしば引数チェックに基づいていることだと思います。したがって、アサーションを使用する場合の次の別の例を検討してください。

build-sorted-list-from-user-input(input)

    throw-exception-if-bad-input(input)

    ...

    //build list using algorithm that you expect to give a sorted list

    ...

    assert(is-sorted(list))

end

入力に例外を使用するのは、時々悪い入力が発生することが予想されるためです。リストがソートされているのは、アルゴリズムのバグを見つけるのに役立つと断言しますが、これは定義上、予期しないものです。アサーションはデバッグ ビルドでのみ行われるため、チェックにコストがかかりますが、ルーチンの呼び出しごとに実行してもかまいません。

運用コードの単体テストは引き続き行う必要がありますが、これは、コードが正しいことを確認する別の補完的な方法です。単体テストでは、ルーチンがインターフェイスに準拠していることを確認しますが、アサーションは、実装が期待どおりに機能していることを確認するためのよりきめ細かい方法です。

于 2009-12-12T22:16:09.963 に答える
8

アサーションは悪ではありませんが、簡単に誤用される可能性があります。「アサーションは、適切なエラー処理と報告について考えることを避けるための松葉杖としてよく使用される」という声明に同意します。私はこれをかなり頻繁に見てきました。

個人的には、アサーションを使用するのが好きです。アサーションは、コードを書いている間に行った可能性のある仮定を文書化するからです。コードを維持している間にこれらの仮定が破られた場合、テスト中に問題を検出できます。ただし、プロダクション ビルドを行うとき (つまり、#ifdefs を使用するとき) には、コードからすべてのアサートを削除するようにしています。本番ビルドでアサーションを削除することにより、誰かがアサーションを松葉杖として悪用するリスクを排除します。

アサーションには別の問題もあります。アサーションは実行時にのみチェックされます。しかし、実行したいチェックがコンパイル時に実行されている場合がよくあります。コンパイル時に問題を検出することをお勧めします。C++ プログラマの場合、boost はこれを可能にする BOOST_STATIC_ASSERT を提供します。C プログラマー向けに、この記事 (リンク テキスト) では、コンパイル時にアサーションを実行するために使用できる手法について説明しています。

要約すると、私が従う経験則は次のとおりです。製品ビルドではアサーションを使用しないでください。可能であれば、コンパイル時に検証できないもの (つまり、実行時にチェックする必要があるもの) に対してのみアサーションを使用します。

于 2009-12-06T16:23:47.397 に答える
5

私は、デバッグとリリースで異なることを行うコードを避けることを好みます。

条件に応じてデバッガーを中断し、すべてのファイル/行情報を取得すると便利ですが、正確な式と正確な値も得られます。

「デバッグでのみ条件を評価する」というアサートを持つことは、パフォーマンスの最適化になる可能性があり、そのため、プログラムの 0.0001% でのみ有用です。他のすべての場合、式は実際にプログラムの状態を変更する可能性があるため、これは有害です。

assert(2 == ShroedingersCat.GetNumEars()); デバッグとリリースでプログラムに異なることをさせるでしょう。

例外をスローするアサート マクロのセットを開発し、デバッグ バージョンとリリース バージョンの両方で実行します。たとえば、ファイル、行、a などの実際の値THROW_UNLESS_EQ(a, 20);の両方を持つ what() メッセージで例外をスローします。これを実行できるのはマクロだけです。デバッガーは、特定の例外タイプの「スロー」で中断するように構成されている場合があります。

于 2009-12-09T13:39:15.470 に答える
5

適切なエラー報告を考慮せずにアサートを使用したことを認めます。ただし、正しく使用すると非常に役立つということはありません。

これらは、「クラッシュ アーリー」の原則に従いたい場合に特に役立ちます。たとえば、参照カウント メカニズムを実装しているとします。コードの特定の場所では、refcount が 0 または 1 でなければならないことがわかっています。また、refcount が間違っていても、プログラムがすぐにクラッシュすることはなく、次のメッセージ ループの時点で、なぜ問題が発生したのかを突き止めるのが難しくなるとします。アサートは、エラーの発生源に近い場所でエラーを検出するのに役立ちました。

于 2009-12-07T07:55:08.927 に答える
5

強く主張するのは嫌いです。悪とまでは言いませんが。

基本的に、アサートは未チェックの例外と同じことを行いますが、唯一の例外は、(通常は) アサートが最終製品のために保持されるべきではないということです。

システムのデバッグおよび構築中に自分自身のセーフティ ネットを構築する場合、顧客、サポート ヘルプ デスク、または現在構築しているソフトウェアを使用するすべての人に対して、なぜこのセーフティ ネットを拒否するのでしょうか。アサートと例外的な状況の両方に対して例外を排他的に使用します。適切な例外階層を作成することで、非常に迅速に 1 つを他のものから識別することができます。今回を除いて、アサートはそのまま残り、失敗した場合に貴重な情報を提供できます。

したがって、アサートを完全に削除し、プログラマーに例外を使用して状況を処理するように強制することで、Go の作成者を完全に理解しています。これには簡単な説明があります。例外は、仕事のためのより良いメカニズムです。なぜ古風なアサートに固執するのですか?

于 2009-12-07T10:03:23.820 に答える
3

私は最近、コードにいくつかのアサートを追加し始めました。これが私が行ってきた方法です。

コードを境界コードと内部コードに精神的に分割します。境界コードは、ユーザー入力を処理し、ファイルを読み取り、ネットワークからデータを取得するコードです。このコードでは、入力が有効な場合 (インタラクティブなユーザー入力の場合) にのみ終了するループで入力を要求するか、回復不能なファイル/ネットワークの破損データの場合に例外をスローします。

内部コードは他のすべてです。たとえば、クラスで変数を設定する関数は、次のように定義できます。

void Class::f (int value) {
    assert (value < end);
    member = value;
}

ネットワークから入力を取得する関数は次のようになります。

void Class::g (InMessage & msg) {
    int const value = msg.read_int();
    if (value >= end)
        throw InvalidServerData();
    f (value);
}

これにより、2 層のチェックが得られます。実行時にデータが決定されるものはすべて、常に例外または即時のエラー処理を取得します。ただし、ステートメントで追加のチェックをClass::f行うassertということは、何らかの内部コードで が呼び出された場合Class::fでも、サニティ チェックが行われることを意味します。私の内部コードは有効な引数を渡さない可能性があります (value複雑な一連の関数から計算した可能性があるため)。そのため、設定関数にアサーションを設定して、関数を呼び出している人に関係なく、valueまたはに等しいend

これは、私がいくつかの場所で読んでいるものに当てはまるようです。アサーションは、正常に機能するプログラムで違反することは不可能であるべきですが、例外はまだ可能である例外的で誤ったケースのためのものであるべきです。理論的にはすべての入力を検証しているため、アサーションがトリガーされることはありません。もしそうなら、私のプログラムは間違っています。

于 2012-04-07T15:48:53.940 に答える
2

あなたが話しているアサーションが、プログラムが嘔吐してから存在することを意味する場合、アサーションは非常に悪い可能性があります。これは、それらが常に間違った使用法であると言っているのではなく、非常に簡単に誤用される構造です。彼らはまた、多くのより良い選択肢を持っています。そのようなものは悪と呼ばれるための良い候補です。

たとえば、サードパーティのモジュール(または実際には任意のモジュール)が呼び出し側プログラムを終了することはほとんどありません。これは、呼び出し側のプログラマーに、その時点でプログラムがとるべきリスクを制御することはできません。多くの場合、データは非常に重要であるため、破損したデータを保存することでさえ、そのデータを失うよりも優れています。アサートにより、データが失われる可能性があります。

主張するためのいくつかの選択肢:

  • デバッガーを使用して、
  • コンソール/データベース/その他のログ
  • 例外
  • 他のタイプのエラー処理

いくつかの参考文献:

アサーションを提唱する人々でさえ、本番環境ではなく開発環境でのみ使用する必要があると考えています。

この人は、例外がスローされた後も持続するデータが破損している可能性がある場合は、assertを使用する必要があると述べています:http ://www.advogato.org/article/949.html 。これは確かに合理的なポイントですが、外部モジュールは、(「for」を終了することによって)呼び出し元のプログラムにとって破損したデータがどれほど重要であるかを決して規定してはなりません。これを処理する適切な方法は、プログラムが現在一貫性のない状態にある可能性があることを明確にする例外をスローすることです。また、優れたプログラムはほとんどがモジュールで構成されているため(メインの実行可能ファイルに小さなグルーコードが含まれている)、ほとんどの場合、アサートは間違った方法です。

于 2010-03-27T06:36:48.517 に答える
1

簡単な答え: いいえ、アサーションは役に立つと思います

于 2009-12-06T04:09:42.517 に答える
1

はい、アサートは悪です。

多くの場合、適切なエラー処理を使用する必要がある場所で使用されます。最初から適切な製品品質のエラー処理を書くことに慣れましょう!

通常、それらは単体テストの作成の邪魔になります (テスト ハーネスと対話するカスタム アサートを作成しない限り)。これは、適切なエラー処理を使用する必要がある場合に使用されることが多いためです。

ほとんどの場合、それらはリリース ビルドからコンパイルされます。つまり、実際にリリースするコードを実行しているときには、それらの「テスト」は利用できません。マルチスレッドの状況では、最悪の問題はリリース コードにしか現れないことが多いため、これは悪いことです。

壊れたデザインの松葉杖になることもあります。つまり、コードの設計により、ユーザーは呼び出されるべきではない方法でコードを呼び出すことができ、アサートはこれを「防止」します。デザイン修正!

これについては、2005 年にこちらのブログで詳しく書いています。

于 2009-12-07T07:56:01.390 に答える
1

assertタイピングが少ないため、エラー処理に悪用されています。

したがって、言語設計者として、より少ないタイピングで適切なエラー処理を実行できることを確認する必要があります。例外メカニズムが冗長であるため、アサートを除外することは解決策ではありません。ちょっと待って、Go にも例外はありません。残念な :)

于 2009-12-11T00:42:34.857 に答える
1

それを見たとき、作者の頭を蹴り飛ばしたくなりました。

私は常にコード内で assert を使用し、最終的にコードをさらに記述するときにすべてを置き換えます。必要なロジックを書いていないときに使用し、プロジェクトが完了に近づくにつれて削除される例外を書く代わりに、コードに遭遇したときに警告を受けたい.

また、例外は、私が嫌いな製品コードにより簡単に溶け込みます。assert は、throw new Exception("Some generic msg or 'pretend i am an assert'");

于 2009-12-16T03:15:58.880 に答える
1

assert を擁護するこれらの回答に関する私の問題は、通常の致命的なエラーと何が違うのか、なぜ assert が例外のサブセットにならないのかを明確に指定していないことです。さて、そうは言っても、例外が決してキャッチされない場合はどうなるでしょうか? それは命名法による主張になりますか?そして、なぜ /nothing/ が処理できない例外を発生させることができるという制限を言語に課したいのでしょうか?

于 2010-01-28T17:48:10.833 に答える
1

assertは非常に便利で、トラブルの最初の兆候でプログラムを停止することにより、予期しないエラーが発生したときに多くの後戻りを省くことができます。

一方で、乱用は非常に簡単assertです。

int quotient(int a, int b){
    assert(b != 0);
    return a / b;
}

適切で正しいバージョンは次のようになります。

bool quotient(int a, int b, int &result){
    if(b == 0)
        return false;

    result = a / b;
    return true;
}

つまり… 長期的には… 全体像として…assert悪用される可能性があることに同意しなければなりません。私はいつもそれをします。

于 2009-12-07T07:43:16.033 に答える
0

一般的に非生産的なほど悪ではありません。永続的なエラー チェックとデバッグは分離されています。アサートは、すべてのデバッグが永続的であるべきだと人々に思わせ、頻繁に使用すると読みやすさに大きな問題を引き起こします。永続的なエラー処理は、必要な場合よりも優れている必要があります。また、アサートは独自のエラーを引き起こすため、かなり疑わしい方法です。

于 2009-12-07T02:33:30.840 に答える
0

私は assert() を決して使用しません。通常、例は次のようになります。

int* ptr = new int[10];
assert(ptr);

これは悪いことです。私は決してこれを行いません。私のゲームが大量のモンスターを割り当てている場合はどうなりますか? なぜゲームをクラッシュさせる必要があるのですか。代わりに、エラーを適切に処理する必要があるため、次のようにします。

CMonster* ptrMonsters = new CMonster[10];
if(ptrMonsters == NULL) // or u could just write if(!ptrMonsters)
{
    // we failed allocating monsters. log the error e.g. "Failed spawning 10 monsters".
}
else
{
    // initialize monsters.
}
于 2012-02-03T03:38:42.083 に答える