Même si Rust fournit son livre Rust, j'ai eu du mal à comprendre le mot-clé mut
. Je me suis demandé : « Suis-je le seul à avoir ce problème ? » Une recherche rapide sur Google a confirmé que je n'étais pas seul.
En conséquence, j'ai décidé d'écrire cet article pour fournir une explication détaillée du mot-clé mut
. Cet article est destiné à ceux qui proviennent de langages de haut niveau comme Python ou JavaScript.
Pour créer une variable non modifiable dans Rust, écrivez simplement let x = 1337
. C'est simple. Si vous souhaitez créer une variable qui pourra être mutée ultérieurement, ajoutez simplement le mot-clé mut
après let
. Rust a une convention utile qui encourage la clarté des intentions.
L'ajout du mot-clé mut
informe les autres que cette variable sera modifiée ailleurs dans le code. D'accord.
Visualisons-le. Deux variables ici, let mut x = 1337
et let y = 42
.
Pour le moment, tout est simple. Cependant, les choses commencent à devenir un peu bouclées lors de l'utilisation de références mut
. Créons-en.
let mut x = 1337; let y = 42; let x_ref = &mut x; let y_ref = &y;
J'ai créé deux références (ou "empruntées" en terme de Rust). L'un d'eux est une référence mutable et l'autre est une référence en lecture seule. Créons à nouveau un schéma pour cela.
Dans le schéma donné, j'ai 4 variables, dont 2 sont des références. Les deux variables de référence sont immuables et n'ont pas de mot-clé mut
après let
, ce qui signifie que je ne peux pas changer ce vers quoi elles pointent. Cependant, je peux toujours modifier la valeur à laquelle ils font référence.
*x_ref = 777;
Si vous écrivez ceci, le compilateur Rust ne se plaindra pas et la valeur de x
(pas la référence elle-même) deviendra 777
. Cependant, il y a un carré rouge sur le schéma indiquant que x_ref
ne dispose pas de l'option de mutabilité. Alors, pourquoi puis-je modifier la valeur à laquelle elle fait référence ?
Revenons au schéma du let x_ref = &mut x
.
Le premier bloc blanc contient le nom : x_ref
. Le second m'informe sur le type stocké dans cette variable. Dans sa forme complète, sans aucune annotation de type implicite, je peux écrire ce qui suit :
let x_ref: &mut i32 = &mut x;
Je peux interpréter cela comme : créons une variable immuable nommée x_ref
qui contiendra une référence mutable à i32
et initialisons-la immédiatement avec la référence mutable à la valeur i32
dans la variable x
.
Cela signifie que je peux modifier la valeur vers laquelle elle pointe, mais je ne peux pas modifier la valeur (ou l'adresse) de la référence. En d'autres termes, je ne peux pas écrire quelque chose comme :
let x_ref: &mut i32 = &mut x; let mut z = 0; x_ref = &mut z; // Not allowed!
En termes de schémas, je souhaite changer la direction vers laquelle pointe la flèche dans le bloc de code ci-dessus. Cependant, même si la variable z
est mutable, je ne peux pas changer la flèche car le problème réside dans l'immuabilité de la x_ref
elle-même.
Pour changer le sens de la flèche, je dois modifier l'adresse stockée dans la variable x_ref
. Cependant, je ne peux pas le faire car la variable est immuable.
Faisons-le!
let mut x: i32 = 1337; let mut x_ref: &mut i32 = &mut x; // I've added mut before x_ref let mut z = 0; x_ref = &mut z; // Allowed!
Il y a trop d'instances de mut
ici autour x_ref
, n'est-ce pas ? Décrivons-les.
let mut x_ref
: je crée une variable mutable nommée x_ref
, ce qui signifie que je peux modifier sa valeur plus tard.
&mut i32
: je déclare que la variable contiendra une ou plusieurs références mutables à une valeur de type i32
.
&mut x
: j'emprunte (obtiens une référence à) la variable x
.
Ensuite, j'ai créé une variable nommée z
et lui ai attribué la valeur 0
. Par la suite, lorsque j'ai écrit x_ref = &mut z
, j'ai indiqué que je comprenais que x_ref
était une variable mutable qui ne pouvait contenir que des références à des valeurs i32
.
Puisque le type de z
est i32
, je peux attribuer son adresse à la variable x_ref
. Pour obtenir l'adresse de z
, j'ai utilisé la syntaxe &mut z
.
Le schéma.
Jetez un oeil à =
dans la déclaration, cela peut paraître un peu évident, mais…
let mut x_ref = &mut x;
… Je le vois comme un diviseur (surtout si vous le faites pivoter de 90 degrés) qui divise l'instruction en deux sous-instructions : gauche et droite. Le côté gauche fournit des informations sur la variable elle-même, tandis que le côté droit nous renseigne sur la valeur .
Lorsque j'utilise l'opérateur de déréférencement *
pour modifier la valeur...
*x_ref = 100;
... Je ne change pas la valeur de la variable x_ref
. Au lieu de cela, je modifie la valeur à laquelle x_ref
fait référence.
J'utilisais fréquemment mut
auparavant. Et si j’en oublie certains ?
let i = 1; let j = 2; let mut k = &i;
Puis-je changer la valeur de i
ici ? En utilisant la technique du diviseur, il est assez simple de répondre. Je peux changer la valeur de k
(je vois mut
sur le côté gauche), mais la valeur (côté droit) est une référence immuable à i
(il n'y a pas mut
ici).
Donc…
let i = 1; let j = 2; let mut k = &i; k = &j; // This is legal. *k = 3; // This is not.
Le schéma.
Dans cet article, nous avons disséqué les nuances du mot-clé et des références mut
. N'oubliez pas qu'il existe une distinction entre une référence mutable et une variable mutable contenant une référence. Notre astuce ?
Utiliser le signe =
comme diviseur mental pour mieux comprendre les missions dans Rust. Cette simple visualisation peut dissiper de nombreuses confusions.
Bon codage !