programmez-en-d/const_et_immutable.whata

588 lines
27 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[set
title = "Immutabilité"
partAs = chapitre
translator = "Raphaël Jakse"
]
Nous avons vu que les variables représentent des idées dans les programmes. Les interactions de ces idées sont matérialisées par des expressions qui changent les valeurs de ces variables~ :
[code=d <<<
// Régler la note
prixTotal = calculerTotal(prixDesElements);
argentDansLePortefeuille -= prixTotal;
argentDuMarchand += prixTotal;
>>>]
Quand on modifie une variable, on dit qu'on la [* mute]. La mutabilité est essentielle pour la plupart des tâches. Cependant, dans certains cas, la mutabilité n'est pas adaptée~ :
- [
Certaines idées sont immuables par définition. Par exemple, il y a toujours sept jours par semaine, la constante pi (π) est constante, un programme peut ne prendre en charge qu'un nombre limité de langages (par ex. seulement français et turc), etc.
]
- [
Si toute variable était modifiable comme nous l'avons vu jusqu'à maintenant, alors tout morceau de code qui l'utiliserait pourrait potentiellement la modifier. Même quand il n'y a pas de raison de modifier une variable lors d'une opération quelconque, il n'y a également aucune garantie que ce n'est pas le cas. Les programmes sont difficiles à lire et à modifier quand il n'y a pas de garantie d'immuabilité.
Par exemple, il peut être clair que l'appel de fonction [c retirer(bureau, employé)] retire un employé d'un bureau. Si toutes les variables étaient mutables, on ne saurait déterminer avec certitude laquelle des deux variables serait modifiée par l'appel de fonction. On peut s'attendre à ce que le nombre d'employés de [c bureau] actifs soit décrémenté, mais est-ce que l'appel de fonction modifie également [c employé] d'une certaine manière~ ?
]
L'idée d'immuabilité aide à comprendre des bouts de programmes en garantissant que certaines opérations ne modifient pas certaines variables. Elle permet aussi de réduire le risque de faire certaines erreurs de programmations.
L'[* immuabilité] en D est représentée par les mots-clés [c const] et [c immutable]. Même si les deux mots sont proches dans leur signification, leurs rôles sont différents et parfois incompatibles.
[ = Variables immuables
Les expressions «~ variables immuables~ » ou «~ variables constantes~ » sont contradictoires quand le mot «~ variable~ » est pris au sens littéral pour désigner [* quelque chose qui change]. Le mot «~ variable~ » désigne n'importe quel élément d'un programme qui peut être mutable ou immuable. [comment <<< Immuable ? Immutable ? >>>]
Il y a trois manières de définir des variables qui ne peuvent jamais être mutées~ :
[ = Constantes [c enum]
Nous avons vu plus tôt dans le [[part:enum | chapitre sur les énumérations]] qu'[c enum] définie des valeurs constantes nommées~ :
[code=d <<<
enum nomFichier = "liste.txt";
>>>]
Tant que leurs valeurs peuvent être déterminées lors de la compilation, les variables [c enum] peuvent aussi être initialisées par des valeurs de retour de fonctions~ :
[code=d <<<
int nbLignesTotal()
{
return 42;
}
int nbColonnesTotal()
{
return 7;
}
string nom()
{
return "liste";
}
void main()
{
enum nomFichier = nom() ~ ".txt";
enum nbCarrésTotal = nbLignesTotal() * nbColonnesTotal();
}
>>>]
Comme on peut s'y attendre, les valeurs des constantes [c enum] ne peuvent pas être modifiées~ :
[code=d <<<
++nbCarrésTotal; // ← ERREUR de compilation
>>>]
Même si c'est une manière très efficace de représenter des valeurs immuables, [c enum] ne peut être utilisé que pour des valeurs connues lors de la compilation.
Une constante [c enum] est une [* constante manifeste], ce qui signifie que le programme est compilé comme si la constante avait été remplacée par sa valeur partout dans le code. Par exemple, considérons la définition énumérée suivante et les deux expressions qui l'utilisent~ :
[code=d <<<
enum i = 42;
writeln(i);
foo(i);
>>>]
Ce code serait le même en remplaçant [c i] par sa valeur 42~ :
[code=d <<<
writeln(42);
foo(42);
>>>]
Même si ce remplacement fait sens pour des types simples comme [c int] et ne fait aucune différence dans le programme, les constantes [c enum] apportent un coût caché quand elles sont utilisées pour des tableaux ou des tableaux associatifs~ :
[code=d <<<
enum a = [ 42, 100 ];
writeln(a);
foo(a);
>>>]
En remplaçant [c a] par sa valeur, le code équivalent que le compilateur compilerait est le suivant~ :
[code=d <<<
writeln([ 42, 100 ]); // Un tableau est créé lors de l'exécution
foo([ 42, 100 ]); // Un autre tableau est créé lors de l'exécution
>>>]
Le coût caché ici est qu'il y aurait deux tableaux créés pour les deux expressions ci-dessus. Pour cette raison, il vaut mieux définir les tableaux et les tableaux associatifs en tant que variables immuables s'ils sont utilisés plusieurs fois dans le programme.
]
[ = Variables [c immutable]
Comme [c enum], ce mot-clé indique que la valeur d'une variable ne changera jamais. Sa différence par rapport à [c enum] est que les valeurs des variables [c immutable] peuvent être calculées lors de l'exécution du programme.
Le programme suivant compare les utilisations de [c enum] et de [c immutable]. Le programme attend que l'utilisateur devine un nombre qui a été choisi au hasard. Comme le nombre aléatoire ne peut pas être déterminé lors de la compilation, il ne peut pas être défini comme un [c enum]. Cependant, comme la valeur choisie ne doit jamais être changée après avoir été choisie, il est correct de marquer cette variable comme [c immutable].
Le programme utilise la fonction [c lire_entier()] définie dans le chapitre précédent~ :
[code=d <<<
import std.stdio;
import std.random;
int lire_entier(string message)
{
int resultat;
write(message, " ? ");
readf(" %s", &resultat);
return resultat;
}
void main()
{
enum min = 1;
enum max = 10;
immutable nombre = uniform(min, max + 1);
writefln("Je pense à un nombre entre %s et %s.",
min, max);
auto estCorrect = false;
while (!estCorrect) {
immutable tentative = lire_entier("Que proposez-vous");
estCorrect = (tentative == nombre);
}
writeln("Correct !");
}
>>>]
Observations~ :
[c min] et [c max] font partie intégrante du comportement du programme et leurs valeurs sont connues lors de la compilation. Pour cette raison, elles sont définie comme des constantes [c enum]. [c nombre] est défini comme [c immutable] parce qu'il ne serait pas approprié de modifier cette variable lors de l'exécution du programme. Idem pour chaque tentative de l'utilisateur~ : une fois qu'elle est lue, la tentative ne devrait pas être modifiée.
Notez que les types de ces variables ne sont pas explicitement spécifiés. Comme avec [c auto], les types des variables [c enum] et [c immutable] peuvent être inférés depuis l'expression à droite de l'opérateur d'affectation.
Même s'il n'est pas nécessaire d'écrire le type complet comme dans par ex. [c immutable(int)], [c immutable] prend normalement le type entre parenthèse. La sortie du programme suivant montre que le nom du type des trois variables est en fait le même~ :
[code=d <<<
import std.stdio;
void main()
{
immutable typeInféré = 0;
immutable int typeExplicite = 1;
immutable(int) typeComplet = 2;
writeln(typeof(typeInféré).stringof);
writeln(typeof(typeExplicite).stringof);
writeln(typeof(typeComplet).stringof);
}
>>>]
Le nom du type inclut [c immutable]~ :
[code=d <<<
immutable(int)
immutable(int)
immutable(int)
>>>]
Le type qui est spécifié entre parenthèses a une signification. Nous verrons cela plus tard quand nous aborderons l'immuabilité d'une tranche entière comparativement à celle de ses éléments.
]
[ = Variables [c const]
Ce mot-clé a le même effet qu'[c immutable] sur les variable. Les variables [c const] ne peuvent pas être modifiées~ :
[code=d <<<
const moitié = total / 2;
moitié = 10; // ← ERREUR de compilation
>>>]
Je vous suggère d'utiliser [c immutable] plutôt que [c const] pour les variables. La raison à cela est que les variables [c immutable] peuvent être passées aux fonctions en paramètre [c immutable]. Nous allons voir cela tout de suite.
]
]
[ = Paramètres immuables
Il est possible pour les fonctions de promettre qu'elles ne vont pas modifier certains paramètres qu'elles prennent. Le compilateur garantit que cette promesse est tenue. Avant de voir comment ça fonctionne, voyons que les fonctions peuvent en effet modifier les éléments des tranches qui sont passées en arguments à ces fonctions.
D'après ce que nous avons vu dans le [[part:tranches | chapitre sur les tranches et autres fonctionnalités des tableaux], les tranches ne stockent pas leurs éléments mais y donnent accès. Il peut y avoir plus d'une tranche à un moment donné qui donne accès aux mêmes éléments.
Même si les exemples de cette section ne se concentrent que sur les tranches, cela s'applique aussi aux tableaux associatifs et aux classes parce que ce sont également des [* types référence].
Une tranche qui est passée en argument à une fonction n'est pas la tranche avec laquelle la fonction est appelée. L'argument est une copie de la tranche~ :
[code=d <<<
import std.stdio;
void main()
{
int[] tranche = [ 10, 20, 30, 40 ]; // 1
moitié(tranche);
writeln(tranche);
}
void moitié(int[] nombres) // 2
{
foreach (ref nombre; nombres) {
nombre /= 2;
}
}
>>>]
Quand l'exécution du programme entre dans la fonction [c moitié()], il y a deux tranches qui donnent accès aux mêmes éléments~ :
- la tranche nommée [c tranche] qui est définie dans [c main()], qui est passée en argument à [c moitié()]~ ;
- la tranche nommée [c nombres] que [c moitié()] reçoit en argument, qui donne accès aux mêmes éléments que [c tranche].
Aussi, à cause du mot-clé [c ref] dans la boucle [c foreach], les valeurs des éléments d'origine (et seulement eux) sont divisées par deux~ :
[output <<<
[5, 10, 15, 20]
>>>]
Il est utile pour les fonctions d'avoir la possibilité de modifier les éléments des tranches qui sont passées en arguments. Certaines fonctions n'existent que pour cela, comme dans cet exemple.
Le compilateur n'autorise pas à passer des variables immuables comme arguments à de telles fonctions, parce qu'il est impossible de modifier une variable immuable~ :
[code=d <<<
immutable int[] tranche = [ 10, 20, 30, 40 ];
moitié(tranche); // ← ERREUR de compilation
>>>]
L'erreur de compilation indique qu'une variable de type [c <<<immutable(int[])>>>] ne peut pas être utilisée comme un argument de type [c <<< int[]>>>]~ :
[output <<<
Error: function essai.moitié (int[] nombres) is not callable
using argument types (immutable(int[]))
>>>]
[ = Paramètres [c const]
Il est important et naturel que le passage de variables [c immutable] à des fonctions comme [c moitié()], qui modifient leurs arguments, soit interdit. Cependant, ne pas pouvoir les passer à des fonctions qui ne modifient pas leurs arguments serait une limitation~ :
[code=d <<<
import std.stdio;
void main()
{
immutable int[] tranche = [ 10, 20, 30, 40 ];
afficher(tranche); // ← ERREUR de compilation
}
void afficher(int[] tranche)
{
writefln("%s éléments: ", tranche.length);
foreach (i, element; tranche) {
writefln("%s: %s", i, element);
}
}
>>>]
Interdire à [c tranche] d'être imprimée uniquement parce qu'elle est immuable n'a pas de sens. La bonne manière de gérer cette situation est d'utiliser les paramètres [c const].
Le mot-clé [c const] indique qu'une variable n'est pas modifiée à travers [* cette référence particulière] (par ex. une tranche) de la variable. Indiquer un paramètre comme [c const] garantie que les éléments de la tranche ne seront pas modifiés à l'intérieur de la fonction. Une fois que [c afficher()] donne cette garantie, le programme peut être compilé~ :
[code=d <<<
afficher(tranche); // maintenant, compile
// ...
void afficher(const int[] tranche)
>>>]
Cette garantie permet de passer aussi bien des variables mutables que des variables immuables en arguments~ :
[code=d <<<
immutable int[] tranche = [ 10, 20, 30, 40 ];
afficher(tranche); // compile
int[] trancheMutable = [ 7, 8 ];
afficher(trancheMutable); // compile
>>>]
Un paramètre qui n'est pas modifié dans une fonction mais qui n'est pas indiqué comme [c const] limite l'utilisabilité de cette fonction. De plus, les paramètres [c const] donnent des informations utiles au programmeur. Savoir qu'une variable ne sera pas modifiée lorsqu'elle est passée à une fonction rend le code plus facile à comprendre. Cela évite également de faire des erreurs puisque le compilateur interdit les modifications sur les paramètres [c const]~ :
[code=d <<<
void afficher(const int[] tranche)
{
tranche[0] = 42; // ← ERREUR de compilation
// ...
}
>>>]
Le programmeur prend alors conscience de l'erreur dans la fonction ou repense la conception et supprime éventuellement l'indicateur [c const].
Le fait que les paramètres [c const] acceptent et les variables mutables et les variables immuables a une conséquence intéressante. Ceci est expliqué plus loin dans la section «~ Un paramètre devrait-il être [c const] ou [c immutable]~ ?~ ».
]
[ = Paramètres [c immutable]
Comme nous l'avons vu à la section précédente, les variables immuables ou non peuvent toutes être passées aux fonctions en paramètre [c const]. Dans un sens, les paramètres [c const] sont accueillants.
En revanche, les paramètres [c immutable] apportent un prérequis important~ : seules les variables [c immutable] peuvent être passées aux fonctions en paramètres [c immutable]~ :
[code=d <<<
void func(immutable int[] tranche)
{
/* ... */
}
void main()
{
immutable int[] immTranche = [ 1, 2 ];
int[] tranche = [ 8, 9 ];
func(immTranche); // compile
func(tranche); // ← ERREUR de compilation
}
>>>]
Pour cette raison, l'indicateur [c immutable] devrait être utilisé uniquement quand ce prérequis est nécessaire. Nous avons en fait déjà utilisé l'indicateur [c immutable] indirectement à travers certains types de chaînes de caractères. Ceci sera couvert plus loin.
Nous avons vu que les paramètres qui sont indiqués comme [c const] ou [c immutable] promettent de ne pas modifier la variable qui est passée en argument. Ceci n'est pertinent que pour les types référence, le problème ne se pose pas pour les types valeurs.
Les [* types référence] et les [* types valeur] seront couverts dans des chapitres ultérieurs. Parmi les types que nous avons vus jusqu'à maintenant, seuls les tranches et les tableaux associatifs sont des types référence~ ; les autres sont des types valeur.
]
]
[ = Un paramètre devrait-il être [c const] ou [c immutable] ?
Les deux sections précédentes peuvent donner l'impression que comme ils sont plus flexibles, les paramètres [c const] devraient être préférés aux paramètres [c immutable]. Ceci n'est pas toujours vrai.
[c const] efface l'information de l'immuabilité de la variable d'origine. Cette information est cachée même au compilateur.
Une conséquence de ceci est que les paramètres [c const] ne peuvent pas être passés en arguments à des fonctions qui prennent des paramètres [c immutable]. Par exemple, la fonction [c foo()] qui suit ne peut pas passer son paramètre [c const] à [c bar()]~ :
[code=d <<<
void main()
{
/* La variable d'origine est immuable */
immutable int[] tranche = [ 10, 20, 30, 40 ];
foo(tranche);
}
/* Une fonction qui prend un paramètre const, dans le
* but d'être plus utile. */
void foo(const int[] tranche)
{
bar(tranche); // ← ERREUR de compilation
}
/* Une fonction qui prend un paramètre immuable pour une
* raison valable. */
void bar(immutable int[] tranche)
{
/* ... */
}
>>>]
[c bar()] nécessite un paramètre [c immutable]. Cependant, on ne sait pas si la variable originale que le paramètre [c const] de [c foo()] référence est immuable ou non.
[p Note~ : | il est clair dans le code qui précède que la variable d'origine dans [c main()] est immuable. Cependant, le compilateur compile les fonctions individuellement sans regarder à tous les endroits depuis lesquels la fonction est appelée. Pour le compilateur, le paramètre [c tranche()] peut se référer à une variable mutable ou à une variable immuable.]
Une solution serait d'appeler [c bar()] avec une copie immuable du paramètre~ :
[code=d <<<
void foo(const int[] tranche)
{
bar(tranche.idup);
}
>>>]
Même s'il s'agit d'une solution raisonnable, elle a le coût d'une copie, qui serait un gâchis dans le cas où la variable d'origine était déjà [c immutable].
Après cette analyse, il devrait être clair qu'utiliser systématiquement des paramètres [c const] ne semble pas la meilleure approche dans toutes les situations. Après tout, si le paramètre [c foo()] a été défini comme [c immutable], il ne devrait pas être nécessaire de le copier avant d'appeler [c bar()]~ :
[code=d <<<
void foo(immutable int[] tranche) // Cette fois-ci, immutable
{
bar(tranche); // Copier n'est plus nécessaire
}
>>>]
Même si le code compile, définir le paramètre en tant qu'[c immutable] a un coût similaire~ : cette fois, une copie immuable de la variable originale est nécessaire lors de l'appel à [c foo()] si cette variable n'était pas [c immutable] à l'origine~ :
[code=d <<<
foo(trancheMutable.idup);
>>>]
Les modèles peuvent aider. (Nous verrons les modèles dans des chapitres ultérieurs). Même si je ne m'attends pas à ce que vous compreniez totalement la fonction suivante à cet endroit du livre, je vous la présente comme une solution à ce problème. La fonction modèle [c foo()] suivante peut être appelée aussi bien avec des arguments mutables qu'immuables. Le paramètre ne sera copié que si que si la variable d'origine était mutable~ ; aucune copie ne sera faite si elle était immuable~ :
[code=d <<<
import std.conv;
// ...
/* Parce qu'elle est un modèle, foo() peut être appelée avec
* des variables mutable et immuable. */
void foo(T)(T[] tranche)
{
/* 'to()' ne fait pas de copie si la variable d'origine est
* déjà immuable. */
bar(to!(immutable T[])(tranche));
}
>>>]
]
[ = Immuabilité de la tranche ''versus'' immuabilité des éléments
Nous avons vu plus haut le type d'une tranche immuable affiché comme [c immutable(int[])]. Comme les parenthèses après [c immutable] l'indiquent, c'est la tranche entière qui est immuable. Une telle tranche ne peut être modifié d'aucune manière~ : les éléments ne peuvent pas être ajoutés ou supprimés, leurs valeurs ne peuvent pas être modifiées et la tranche ne peut pas se mettre à donner accès à un autre ensemble d'éléments~ :
[code=d <<<
immutable int[] immTranche = [ 1, 2 ];
immTranche ~= 3; // ← ERREUR de compilation
immTranche[0] = 3; // ← ERREUR de compilation
immTranche.length = 1; // ← ERREUR de compilation
immutable int[] immAutreTranche = [ 10, 11 ];
immTranche = immAutreTranche; // ← ERREUR de compilation
>>>]
Pousser cette immuabilité à un tel extrême peut ne pas être adapté dans tous les cas. Dans la plupart des cas, ce qui est important est seulement l'immuabilité des éléments. Comme une tranche est seulement un outil d'accès aux éléments, effectuer des changements sur la tranche elle-même ne devrait pas poser de problème tant que les éléments ne sont pas modifiés.
Pour indiquer que seuls les éléments sont immuables, le type des éléments (seul) est écrit entre parenthèses. Quand le code est modifié de cette manière, seuls les éléments sont immuables, pas la tranche elle-même~ :
[code=d <<<
immutable(int)[] immTranche = [ 1, 2 ];
immTranche ~= 3; // peut ajouter des éléments
immTranche[0] = 3; // ← ERREUR de compilation
immTranche.length = 1; // peut perdre des éléments
immutable int[] immAutreTranche = [ 10, 11 ];
immTranche = immAutreTranche; // peut donner accès à d'autres éléments
>>>]
Même si les deux syntaxes sont très similaires, elles ont des significations différentes. En résumé~ :
[code=d <<<
immutable int[] a = [1]; /* Ni les éléments, ni la
* tranche ne peuvent être modifiés */
immutable(int[]) b = [1]; /* identique à la précédente */
immutable(int)[] c = [1]; /* Les éléments ne peuvent pas être
* modifiés mais la tranche peut l'être */
>>>]
Cette distinction se retrouve dans certain programmes que nous avons rencontrés. Comme vous devez vous en souvenir, les trois alias de chaînes de caractères impliquent l'immuabilité~ :
- [c string] est un alias de [c immutable(char)~[~]]~ ;
- [c wstring] est un alias de [c immutable(wchar)~[~]]~ ;
- [c dstring] est un alias de [c immutable(dchar)~[~]].
De même, les littéraux de chaînes sont également immuables~ :
- Le type du littéral [c "hello"c] est [c string]~ ;
- Le type du littéral [c "hello"w] est [c wstring]~ ;
- Le type du littéral [c "hello"d] est [c dstring].
Selon ces définitions, les chaînes D sont normalement des tableaux de caractères immuables.
]
[ = [c .dup] et [c .idup]
Il peut y avoir des problèmes d'immuabilité quand les chaînes sont passés aux fonctions en paramètres. Les propriétés [c .dup] et [c .idup] font des copies des tableaux avec l'immuabilité désirée~ :
- [c .dup] fait une copie mutable du tableau (son nom vient de [c "dupliquer"])~ ;
- [c .idup] fait une copie [c immuable] du tableau.
Par exemple, une fonction qui insiste sur l'immuabilité d'un paramètre peut devoir être appelée avec une copie immuable d'une chaîne mutable~ :
[code=d <<<
void foo(string s)
{
// ...
}
void main()
{
char[] salutation;
foo(salutation); // ← ERREUR de compilation
foo(salutation.idup); // ← ceci compile
}
>>>]
]
[ = Utilisation
- [
En règle générale, préférez les variables immuables aux variables mutables.
]
- [
Définissez les valeurs constantes comme [c enum] si elles peuvent être calculées lors de la compilation. Par exemple, la valeur constante représentant le nombre de secondes par minute peut être une [c enum]~ :
[code=d <<<
enum int secondesParMinute = 60;
>>>]
Il n'y a pas besoin de spécifier le type de façon explicite s'il peut être inféré depuis le côté droit de l'affectation~ :
[code=d <<<
enum secondesParMinute = 60;
>>>]
]
- [
Considérez le coût de copie des tableaux (associatifs) [c enum]. Définissez-les commes des variables [c immutable] si les tableaux sont larges et qu'ils sont utilisés plus d'une fois dans le programme.
]
- [
Indiquez les variables comme [c immutable] si leurs valeurs ne changeront pas mais qu'elles ne peuvent pas être connues lors de la compilation. De même, le type peut être inféré~ :
[code=d <<<
immutable tentative = lire_entier("Que proposez-vous");
>>>]
]
- [
Si une fonction ne modifie pas un paramètre, indiquez ce paramètre comme [c const]. Ceci permettra de passer en argument aussi bien des variables mutables qu'immuables~ :
[code=d <<<
void foo(const char[] s)
{
// ...
}
void main()
{
char[] chaineMutable;
string chaineImmuable;
foo(chaineMutable); // ← compile
foo(chaineImmuable); // ← compile
}
>>>]
]
- Pour compléter la règle précédente, considérez que les paramètres [c const] ne peuvent pas être passés aux fonctions qui prennent des [c immutable]. Référez-vous à la section «~ Un paramètre devrait-il être [c const] ou [c immuable]~ ?~ ».
- [
Si la fonction modifie un paramètre, laissez le paramètre comme mutable ([c const] or [c immutable] ne permettrait de toute façon pas les modifications)~ :
[code=d <<<
import std.stdio;
void inverser(dchar[] s)
{
foreach (i; 0 .. s.length / 2) {
immutable temp = s[i];
s[i] = s[$ - 1 - i];
s[$ - 1 - i] = temp;
}
}
void main()
{
dchar[] salutation = "salut"d.dup;
inverser(salutation);
writeln(salutation);
}
>>>]
La sortie~ :
[output <<<
tulas
>>>]
]
]
[ = Résumé
- Les variables [c enum] représentent des idées immuables connues lors de la compilation.
- Les variables [c immutable] representent des idées immuables qui doivent être calculés lors de l'exécution.
- Les paramètres [c const] sont ceux que les fonctions ne modifient pas. Les variables mutable et immuables peuvent toutes être passées comme arguments de paramètres [c const].
- Un paramètre ne doit être déclaré [c immutable] que si cela répond à un impératif spécifique à l'intérieur de la fonction.. Seules les variables [c immutable] peuvent être passées comme arguments de paramètres [c immutable].
- [c <<<immutable(int[])>>>] indique que ni la tranche ni ses éléments ne peuvent être modifiés.
- [c <<<immutable(int)[]>>>] indique que seuls les éléments ne peuvent être modifiés.
- L'immuabilité est un outil très puissant en programmation. Il est utile de savoir que les variables et les paramètres ne sont pas modifiés dans des contextes spécifiques ou lors d'opérations spécifiques.
]