web-dev-qa-db-fra.com

L'affirmation est-elle mauvaise?

Les créateurs de langage Goécriture :

Go ne fournit pas d'assertions. Elles sont indéniablement pratiques, mais notre expérience a été que les programmeurs les utilisent comme une béquille pour éviter de penser à une gestion correcte des erreurs et rapports. Une gestion correcte des erreurs signifie que les serveurs continuent de fonctionner après des erreurs non fatales au lieu de planter. Un rapport d'erreurs approprié signifie que les erreurs sont directes et directes, ce qui évite au programmeur d'interpréter une grande trace de plantage. Les erreurs précises sont particulièrement importantes lorsque le programmeur qui voit les erreurs n'est pas familier avec le code.

Quelle est votre opinion à ce sujet?

195
Frank

Non, il n'y a rien de mal avec assert tant que vous l'utilisez comme prévu.

Autrement dit, il est censé être utilisé pour intercepter des cas qui "ne peuvent pas se produire", lors du débogage, par opposition à la gestion normale des erreurs.

  • Assert: un échec dans la logique du programme lui-même.
  • Gestion des erreurs: une entrée ou un état système erroné non dû à un bogue dans le programme.
318
caf

Non, ni goto ni assert ne sont mauvais. Mais les deux peuvent être mal utilisés.

Assert est pour les vérifications de santé mentale. Les choses qui devraient tuer le programme si elles ne sont pas correctes. Pas pour la validation ou pour remplacer la gestion des erreurs.

106
gahooa

Selon cette logique, les points d'arrêt sont également mauvais.

Les assertions doivent être utilisées comme aide au débogage, et rien d'autre. "Mal" est lorsque vous essayez de les utiliser à la place de gestion des erreurs.

Les assertions sont là pour vous aider, le programmeur, à détecter et résoudre les problèmes qui ne doivent pas exister et vérifier que vos hypothèses restent vraies.

Ils n'ont rien à voir avec la gestion des erreurs, mais malheureusement, certains programmeurs les abusent en tant que tels, puis les déclarent "mauvais".

61
jalf

J'aime beaucoup utiliser l'affirmation. Je trouve cela très utile lorsque je crée des applications pour la première fois (peut-être pour un nouveau domaine). Au lieu de faire une vérification d'erreur très sophistiquée (que je considérerais comme une optimisation prématurée), je code rapidement et j'ajoute beaucoup d'assertions. Après en savoir plus sur le fonctionnement des choses, je fais une réécriture et je supprime certaines des assertions et les modifie pour une meilleure gestion des erreurs.

En raison des assertions, je passe beaucoup moins de temps à coder/déboguer les programmes.

J'ai également remarqué que les asserts m'aident à penser à beaucoup de choses qui pourraient casser mes programmes.

39
arhuaco

Ils doivent être utilisés pour détecter les bogues dans le programme. Pas mauvaise entrée utilisateur.

S'ils sont utilisés correctement, ils sont pas mauvais.

30
Alex Budovski

Comme information supplémentaire, go fournit une fonction intégrée panic. Cela peut être utilisé à la place de assert. Par exemple.

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

panic affichera la trace de la pile, donc d'une certaine manière, elle a pour but assert.

30

Cela revient souvent, et je pense qu'un problème qui rend les défenses d'assertions déroutantes est qu'elles sont souvent basées sur la vérification des arguments. Considérez donc cet exemple différent de cas où vous pourriez utiliser une assertion:

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

Vous utilisez une exception pour l'entrée, car vous vous attendez parfois à une mauvaise entrée. Vous affirmez que la liste est triée pour vous aider à trouver un bogue dans votre algorithme, ce que vous ne vous attendez pas par définition. L'assertion est uniquement dans la version de débogage, donc même si la vérification est coûteuse, cela ne vous dérange pas de le faire à chaque appel unique de la routine.

Vous devez toujours tester à l'unité votre code de production, mais c'est une manière différente et complémentaire de vous assurer que votre code est correct. Les tests unitaires garantissent que votre routine est à la hauteur de son interface, tandis que les assertions sont un moyen plus précis de vous assurer que votre implémentation fait exactement ce que vous attendez d'elle.

13
jtolle

Les affirmations ne sont pas mauvaises, mais elles peuvent être facilement utilisées à mauvais escient. Je suis d'accord avec l'affirmation selon laquelle "les assertions sont souvent utilisées comme une béquille pour éviter de penser à une gestion et à un rapport appropriés des erreurs". Je l'ai vu assez souvent.

Personnellement, j'aime utiliser les assertions car elles documentent les hypothèses que j'ai pu faire en écrivant mon code. Si ces hypothèses sont brisées lors de la maintenance du code, le problème peut être détecté pendant le test. Cependant, je fais le point de supprimer toutes les assertions de mon code lors d'une génération de production (c'est-à-dire en utilisant #ifdefs). En supprimant les assertions dans la version de production, j'élimine le risque que quelqu'un les utilise à mauvais escient comme béquille.

Il y a aussi un autre problème avec les assertions. Les assertions ne sont vérifiées qu'au moment de l'exécution. Mais il arrive souvent que la vérification que vous souhaitez effectuer ait pu être effectuée au moment de la compilation. Il est préférable de détecter un problème au moment de la compilation. Pour les programmeurs C++, boost fournit BOOST_STATIC_ASSERT qui vous permet de le faire. Pour les programmeurs C, cet article ( texte du lien ) décrit une technique qui peut être utilisée pour effectuer des assertions au moment de la compilation.

En résumé, la règle générale que je respecte est la suivante: n'utilisez pas d'assertions dans une génération de production et, si possible, n'utilisez des assertions que pour des éléments qui ne peuvent pas être vérifiés au moment de la compilation (c'est-à-dire qui doivent être vérifiés au moment de l'exécution).

8
figurassa

Je préfère éviter le code qui fait différentes choses dans le débogage et la publication.

Rompre le débogueur à une condition et avoir toutes les informations de fichier/ligne est utile, aussi l'expression exacte et la valeur exacte.

Avoir une affirmation qui "évaluerait la condition uniquement dans le débogage" peut être une optimisation des performances et, en tant que telle, utile uniquement dans 0,0001% des programmes - où les gens savent ce qu'ils font. Dans tous les autres cas, cela est nocif, car l'expression peut en fait changer l'état du programme:

assert(2 == ShroedingersCat.GetNumEars()); obligerait le programme à faire différentes choses dans le débogage et la publication.

Nous avons développé un ensemble de macros d'assertion qui lèveraient une exception, et le font dans les versions de débogage et de version. Par exemple, THROW_UNLESS_EQ(a, 20); lèverait une exception avec le message what () ayant à la fois fichier, ligne et valeurs réelles de a, et ainsi de suite. Seule une macro aurait le pouvoir pour cela. Le débogueur peut être configuré pour s'arrêter au "lancer" du type d'exception spécifique.

5

J'avoue avoir utilisé des assertions sans envisager de rapport d'erreur approprié. Cependant, cela n'enlève pas qu'ils sont très utiles lorsqu'ils sont utilisés correctement.

Ils sont particulièrement utiles si vous souhaitez suivre le principe "Crash Early". Par exemple, supposons que vous implémentez un mécanisme de comptage de références. À certains endroits de votre code, vous savez que le décompte doit être zéro ou un. Et supposez également que si le refcount est erroné, le programme ne se bloquera pas immédiatement, mais au cours de la prochaine boucle de message, il sera difficile de savoir pourquoi les choses ont mal tourné. Une assertion aurait été utile pour détecter l'erreur plus près de son origine.

5
StackedCrooked

Je n'aime pas les affirmations intensément. Mais je n'irais pas jusqu'à dire qu'ils sont mauvais.

Fondamentalement, une assertion fera la même chose qu'une exception non vérifiée, la seule exception est que l'assertion ne doit pas (normalement) être conservée pour le produit final.

Si vous créez un filet de sécurité pour vous-même pendant le débogage et la construction du système, pourquoi refuseriez-vous ce filet de sécurité à votre client, à votre service d'assistance ou à toute personne qui pourra utiliser le logiciel que vous êtes en train de créer. Utilisez des exceptions exclusivement pour les assertions et les situations exceptionnelles. En créant une hiérarchie d'exceptions appropriée, vous pourrez discerner très rapidement les uns des autres. Sauf que cette fois, l'assertion reste en place et peut fournir des informations précieuses en cas de défaillance qui serait autrement perdue.

Je comprends donc parfaitement les créateurs de Go en supprimant complètement les assertions et en forçant les programmeurs à utiliser des exceptions pour gérer la situation. Il y a une explication simple à cela, les exceptions sont juste un meilleur mécanisme pour le travail pourquoi s'en tenir aux assertions archaïques?

5
Newtopian

Réponse courte: Non, je pense que les affirmations peuvent être utiles

3
Brendan

J'ai récemment commencé à ajouter des assertions à mon code, et voici comment je l'ai fait:

Je divise mentalement mon code en code frontière et code interne. Le code frontière est un code qui gère les entrées utilisateur, lit les fichiers et obtient les données du réseau. Dans ce code, je demande une entrée dans une boucle qui ne se termine que lorsque l'entrée est valide (dans le cas d'une entrée utilisateur interactive), ou lève des exceptions dans le cas de données de fichier/réseau corrompues irrécupérables.

Le code interne est tout le reste. Par exemple, une fonction qui définit une variable dans ma classe peut être définie comme

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

et une fonction qui obtient une entrée d'un réseau peut se lire comme telle:

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

Cela me donne deux niveaux de contrôles. Tout ce qui détermine les données au moment de l'exécution reçoit toujours une exception ou une gestion immédiate des erreurs. Cependant, cette vérification supplémentaire dans Class::f avec l'instruction assert signifie que si un code interne appelle un jour Class::f, J'ai encore un contrôle de santé mentale. Mon code interne peut ne pas passer d'argument valide (car j'ai peut-être calculé value à partir d'une série complexe de fonctions), donc j'aime avoir l'assertion dans la fonction de réglage pour documenter cela, quel que soit le nom de la fonction, value ne doit pas être supérieur ou égal à end.

Cela semble correspondre à ce que je lis à quelques endroits, à savoir que les affirmations devraient être impossibles à violer dans un programme qui fonctionne bien, tandis que les exceptions devraient être pour les cas exceptionnels et erronés qui sont encore possibles. Parce qu'en théorie je valide toutes les entrées, il ne devrait pas être possible de déclencher mon assertion. Si c'est le cas, mon programme est erroné.

3
David Stone

Mon problème avec ces réponses défendant l'assertion est que personne ne spécifie clairement ce qui le rend différent d'une erreur fatale régulière , et pourquoi une assertion ne peut pas être un sous-ensemble d'une exception . Maintenant, ceci étant dit, que se passe-t-il si l'exception n'est jamais interceptée? Cela en fait-il une affirmation par nomenclature? Et, pourquoi voudriez-vous jamais imposer une restriction dans le langage qu'une exception peut être levée que/rien/peut gérer?

1
Evan Carroll

Si les affirmations dont vous parlez signifient que le programme vomit puis existe, les affirmations peuvent être très mauvaises. Cela ne veut pas dire qu'ils sont toujours la mauvaise chose à utiliser, ils sont une construction qui est très facilement mal utilisée. Ils ont également de bien meilleures alternatives. Des choses comme ça sont de bons candidats pour être appelées mal.

Par exemple, un module tiers (ou n'importe quel module vraiment) ne devrait presque jamais quitter le programme appelant. Cela ne donne au programmeur appelant aucun contrôle sur le risque que le programme devrait prendre à ce moment. Dans de nombreux cas, les données sont si importantes que même la sauvegarde de données corrompues est préférable à la perte de ces données. Les assertions peuvent vous obliger à perdre des données.

Quelques alternatives aux assertions:

  • À l'aide d'un débogueur,
  • Console/base de données/autre journalisation
  • Exceptions
  • Autres types de gestion des erreurs

Quelques références:

Même les personnes qui plaident pour l'affirmation pensent qu'elles ne devraient être utilisées que dans le développement et pas dans la production:

Cette personne dit que les assertions doivent être utilisées lorsque le module contient des données potentiellement corrompues qui persistent après qu'une exception est levée: http://www.advogato.org/article/949.html . C'est certainement un point raisonnable, cependant, un module externe ne doit jamais prescrire l'importance des données corrompues pour le programme appelant (en quittant "pour") . La bonne façon de gérer cela est de lever une exception qui indique clairement que le programme peut maintenant être dans un état incohérent. Et puisque les bons programmes sont principalement constitués de modules (avec un peu de code glue dans l'exécutable principal), les assertions sont presque toujours la mauvaise chose à faire.

1
hhafez

assert est très utile et peut vous faire économiser beaucoup de recul lorsque des erreurs inattendues se produisent en arrêtant le programme dès les premiers signes de problème.

D'un autre côté, il est très facile d'abuser de assert.

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

La version correcte et correcte serait quelque chose comme:

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

    result = a / b;
    return true;
}

Donc ... à long terme ... dans l'ensemble ... Je dois convenir que assert peut être abusé. Je le fais tout le temps.

1
Agnel Kurian

Oui, les affirmations sont mauvaises.

Souvent, ils sont utilisés dans des endroits où une gestion appropriée des erreurs doit être utilisée. Habituez-vous à écrire dès le début une gestion correcte des erreurs de qualité de production!

Habituellement, ils gênent l'écriture de tests unitaires (sauf si vous écrivez une assertion personnalisée qui interagit avec votre faisceau de test). Cela est souvent dû au fait qu'ils sont utilisés là où une gestion des erreurs appropriée doit être utilisée.

La plupart du temps, ils sont compilés à partir des versions de versions, ce qui signifie qu'aucun de leurs "tests" n'est disponible lorsque vous exécutez le code que vous publiez réellement; étant donné que dans les situations multi-thread, les pires problèmes n'apparaissent souvent que dans le code de version, cela peut être mauvais.

Parfois, ils sont une béquille pour des conceptions autrement cassées; c'est-à-dire que la conception du code permet à un utilisateur de l'appeler d'une manière qu'il ne devrait pas être appelé et l'affirmation "l'empêche". Réparez le design!

J'ai écrit à ce sujet plus sur mon blog en 2005 ici: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html

1
Len Holgate

assert est utilisé à mauvais escient pour la gestion des erreurs car il utilise moins de frappe.

Ainsi, en tant que concepteurs de langage, ils devraient plutôt voir qu'une gestion correcte des erreurs peut être effectuée avec une saisie encore moins importante. Exclure assert parce que votre mécanisme d'exception est détaillé n'est pas la solution. Oh, attendez, Go n'a pas d'exceptions non plus. Dommage :)

1
akuhn

J'avais envie de donner un coup de pied à l'auteur dans la tête quand j'ai vu ça.

J'utilise des assertions tout le temps dans le code et éventuellement les remplace toutes lorsque j'écris plus de code. Je les utilise lorsque je n'ai pas écrit la logique requise et je souhaite être alerté lorsque je rencontre le code au lieu d'écrire une exception qui sera supprimée à mesure que le projet se rapprochera.

Les exceptions se fondent également plus facilement dans le code de production que je n'aime pas. Une assertion est plus facile à remarquer que throw new Exception("Some generic msg or 'pretend i am an assert'");

1
user34537

je n'utilise jamais assert (), les exemples montrent généralement quelque chose comme ceci:

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

C'est mauvais, je ne fais jamais ça, et si mon jeu alloue un tas de monstres? pourquoi devrais-je planter le jeu, vous devriez plutôt gérer les erreurs avec élégance, alors faites quelque chose comme:

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.
}
0
Bob

Pas tant de mal que généralement contre-productif. Il y a une séparation entre la vérification permanente des erreurs et le débogage. Assert incite les gens à penser que tout débogage doit être permanent et provoque d'énormes problèmes de lisibilité lorsqu'ils sont beaucoup utilisés. La gestion permanente des erreurs devrait être meilleure que celle qui est nécessaire, et puisque assert provoque ses propres erreurs, c'est une pratique assez discutable.

0
Charles Eli Cheese