Programmation par contrats

La programmation par contrats en D est un ensemble de fonctionnalités qui permettent d'améliorer la qualité du code en implémentant des tests pour s'assurer que le code se comporte comme on le souhaite. Les contrats ne sont disponibles qu'en mode debug et ne seront pas exécutés en mode release. Ils ne doivent donc pas être utilisés pour valider une entrée de l'utilisateur.

assert

La forme la plus simple de programmation par contrats en D est l'utilisation d'expressions assert(...) qui vérifient qu'une certaine condition est remplie, et déclenchent une AssertionError dans le cas contraire:

assert(sqrt(4) == 2);
// on peut associer un message d'erreur à une expression assert
assert(sqrt(16) == 4, "sqrt ne fonctionne pas !");

Les contrats dans les fonctions

in et out permettent de formaliser des contrats pour vérifier les entrées et/ou les sorties de fonctions.

long racineCarree(long x)
in
{
    // On vérifie que l'argument est valide
    assert(x >= 0);
}
out
{
    // On vérifie que notre résultat est cohérent
    assert((result*result) <= x
        && (result+1) * (result+1) > x);
}
body
{
    return cast(long)std.math.sqrt(cast(real)x);
}

Le contenu du bloc in pourrait aussi être placé au début du corps de la fonction, mais c'est bien plus clair de cette façon. Le bloc out peut capturer la valeur de la retour de la fonction en utilisant la syntaxe out(result).

Vérification d'invariants

invariant() est une méthode spéciale des struct et des class qui permet d'assurer l'intégrité d'un objet au cours de son existence.

  • Elle est appelée après l'appel au constructeur et avant l'appel au destructeur.
  • Elle est appelée avant l'appel à une méthode
  • Elle est appelée après l'appel à une méthode

Valider des entrées utilisateur

Si tous les contrats sont retirés dans une version release, il faut faire attention à ne pas vérifier les données entrées par l'utilisateur dans les contrats.

À noter également qu'il est possible de mettre des expressions assert dans des fonctions nothrow puisqu'elles déclenchent des Errors et non des Exceptions. L'équivalent de assert à l'exécution est la fonction std.exception.enforce, qui déclenche des Exceptions que l'ont peut intercepter et gérer.

Pour aller plus loin

rdmd playground.d