par , 25/03/2018 à 20h43 (15787 Affichages)
L'erreur à un milliard de dollars
Connaissez-vous Tony Hoare ? Il y a de grandes chances que non. Pourtant, en tant que développeurs, vous utilisez très certainement le fruit de ses travaux. L'un d'entre eux, qu'il qualifie lui-même comme "l'erreur à 1 milliard de dollars", est le pointeur nul, objet du billet d'aujourd'hui.
Très pratique, le pointeur nul est présent dans de nombreux langages. C++, Java, C# pour ne prendre que les exemples de langages les plus répandus.
Par exemple, en C#, la valeur nulle est la valeur par défaut des types références. Si vous allouez un tableau d'objets, chaque case du tableau est initialisée… à nulle ! Ou encore, lorsqu'on souhaite préciser qu'une variable n'a pas de valeur, on l'initialise là aussi à nulle.
Mais qui n'a jamais eu à souffrir, durant l'exécution de son code, d'un NullPointerException ? Que le développeur à qui cela n'est jamais arrivé lève la main ! Cette erreur est fréquente (sans doute l'erreur la plus répandue !) et indique l'absence de valeur là où on en attend une. Cela oblige donc à tester la valeur de la variable avant de l'utiliser.
Et c'est là que réside le problème de la plupart des langages ayant cette notion. Il n'y a rien pour distinguer une valeur nulle fortuite d'une valeur nulle voulue.
Évolution du langage C#
Le lien avec C#8 ? Il y a actuellement de longues discussions autour d'une fonctionnalité portant le nom de "référence nullable"(nullable reference dans la langue de Shakespeare), qui permettrait justement de préciser si une référence peut être nulle ou non.
Si on passe outre l'aspect rupture du langage, quels seraient les avantages d'une telle approche ? Et bien le principal avantage est de permettre de corriger de nombreux bogues à la compilation, et non plus à l'exécution.
Ainsi, un code comme
1 2 3 4
| if (hello.Length == 0)
{
} |
Générera une erreur si hello est une variable de type String?, mais pas d'erreur si elle est de type String.
Pour un type String?, il faudra vérifier que la référence est non nulle
1 2 3 4
| if (hello != null && hello.Length == 0)
{
} |
En théorie, c'est très intéressant, mais cela soulève encore de nombreux problèmes, dont toutes les questions n'ont pas encore trouvé réponse. Par exemple, que se passe-t-il si une méthode teste le caractère nul d'une variable ? Par exemple :
1 2 3 4 5
| String? Hello = "world";
if (!String.IsNullOrEmpty(hello))
{
int taille = hello.Length; // Générera une erreur !
} |
Pourquoi une erreur ? Nous, en tant que développeurs, nous savons que la méthode String.IsNullOrEmpty teste le caractère nul ou vide d'une chaîne de caractères. Mais le compilateur ne le sait pas lui. Pour résoudre ce problème, deux approches sont envisageables :
- La première, rajouter un test de nullité dans la conditionnelle. Pas terrible. En plus d'être redondant, cela alourdit le code ;
- La seconde, introduire une nouvelle syntaxe, permettant de dire au compilateur "OK, je sais ce que je fais, ne génère pas d'erreur" (par exemple, int taille = hello.Length!;).
Des zones d'ombre...
De plus, si cette approche permettrait de résoudre certains problèmes, elle n'est pas sans en poser d'autres, qu'il va aussi falloir résoudre avant de pouvoir déployer cette fonctionnalité :
- une valeur (notamment une propriété) peut très bien devenir nulle entre le moment où elle a été testée et où la valeur est effectivement utilisée ;
- des méthodes qui retournent des valeurs non nulles pourront malgré tout retourner des valeurs nulles (cas d'une API qui n'aurait pas été mise à jour par exemple).
Une évolution ou une révolution ?
Le plus gros problème de cette approche reste qu'elle implique une évolution même du langage, évolution "cassante" changeant la sémantique de tout code écrit jusqu'à aujourd'hui. On comprend donc les longues discussions qu'il peut y avoir à ce sujet.
Illustrons ceci avec quelques exemples. Aujourd'hui, ce code est tout à fait valide :
1 2
| String hello = "World";
Hello = null; |
Demain, ce code ne sera plus valide, et générera une erreur au niveau de la seconde ligne, lors de l'affectation de la valeur null.
Autrement dit, par défaut, les références ne seront plus nullables. Pour retrouver le comportement actuel, il faudra déclarer les variables comme étant nullables :
1 2
| String? hello = "World";
Hello = null; // Opération valide, puisque hello est nullable. |
Pour éviter de casser tout le code existant, ce changement ne générera que des avertissements, et non des erreurs. Mais un code propre d'aujourd'hui pourra se retrouver avec des milliers d'avertissements demain !
Affaire à suivre
Les discussions sont loin d'être terminées. On parle déjà de C#8 alors que la prochaine version attendue de C# est la 7.3. Mais ce changement est loin d'être anodin et il est donc nécessaire de l'anticiper largement en amont, car s'il venait à être adopté, cela sera un changement majeur du langage comme jamais il n'en a connu jusqu'à aujourd'hui.
Sources
le projet C# sur github
exemple de discussion sur Reddit (213 commentaires au moment de l'écriture de ce billet, ce qui est énorme !)