Les variables et la mutabilité
Comme mentionné dans la section « Stocker des valeurs dans des variables », par défaut, les variables sont immuables. C’est l’un des nombreux coups de pouce que Rust vous donne pour écrire votre code d’une manière qui tire parti de la sécurité et de la concurrence facile qu’offre Rust. Cependant, vous avez toujours la possibilité de rendre vos variables mutables. Explorons comment et pourquoi Rust vous encourage à privilégier l’immuabilité et pourquoi parfois vous voudrez peut-être vous désengager.
Lorsqu’une variable est immuable, une fois qu’une valeur est liée à un nom, vous ne pouvez pas modifier cette valeur. Pour illustrer cela, générez un nouveau projet appelé variables dans votre répertoire projects en utilisant cargo new variables.
Ensuite, dans votre nouveau répertoire variables, ouvrez src/main.rs et remplacez son code par le code suivant, qui ne compilera pas encore :
Fichier : src/main.rs rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-03-associated-functions/src/main.rs:here}}
fn main() {
let x = 5;
println!("The value of x is: {x}");
x = 6;
println!("The value of x is: {x}");
}
Enregistrez et exécutez le programme avec cargo run. Vous devriez recevoir un message d’erreur concernant une erreur d’immuabilité, comme le montre cette sortie : console {{#include ../listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable/output.txt}}
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
error[E0384]: cannot assign twice to immutable variable `x`
--> src/main.rs:4:5
|
2 | let x = 5;
| - first assignment to `x`
3 | println!("The value of x is: {x}");
4 | x = 6;
| ^^^^^ cannot assign twice to immutable variable
|
help: consider making this binding mutable
|
2 | let mut x = 5;
| +++
For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables` (bin "variables") due to 1 previous error
Cet exemple montre comment le compilateur vous aide à trouver les erreurs dans vos programmes. Les erreurs de compilation peuvent être frustrantes, mais en réalité elles signifient seulement que votre programme ne fait pas encore ce que vous voulez qu’il fasse de manière sûre ; elles ne signifient pas que vous n’êtes pas un bon programmeur ! Les Rustacés expérimentés obtiennent eux aussi des erreurs de compilation.
Vous avez reçu le message d’erreur cannot assign twice to immutable variable `x` parce que vous avez essayé d’assigner une seconde valeur à la variable immuable x.
Il est important que nous obtenions des erreurs à la compilation lorsque nous essayons de modifier une valeur désignée comme immuable, car cette situation peut mener à des bogues. Si une partie de notre code fonctionne en supposant qu’une valeur ne changera jamais et qu’une autre partie de notre code modifié cette valeur, il est possible que la première partie du code ne fasse pas ce pour quoi elle a été conçue. La cause de ce type de bogue peut être difficile à retrouver après coup, surtout lorsque la seconde portion de code ne modifié la valeur que parfois. Le compilateur Rust garantit que lorsque vous déclarez qu’une valeur ne changera pas, elle ne changera vraiment pas, de sorte que vous n’avez pas à en garder la trace vous-même. Votre code est ainsi plus facile à comprendre.
Mais la mutabilité peut être très utile et peut rendre le code plus pratique à écrire. Bien que les variables soient immuables par défaut, vous pouvez les rendre mutables en ajoutant mut devant le nom de la variable, comme vous l’avez fait au [chapitre 2][storing-values-with-variables]. L’ajout de mut transmet également une intention aux futurs lecteurs du code en indiquant que d’autres parties du code modifieront la valeur de cette variable.
Par exemple, modifions src/main.rs comme suit :
Fichier : src/main.rs rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-03-associated-functions/src/main.rs:here}}
fn main() {
let mut x = 5;
println!("The value of x is: {x}");
x = 6;
println!("The value of x is: {x}");
}
Lorsque nous exécutons le programme maintenant, nous obtenons ceci : console {{#include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/output.txt}}
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/variables`
The value of x is: 5
The value of x is: 6
Nous sommes autorisés à changer la valeur liée à x de 5 à 6 lorsque mut est utilisé. En fin de compte, le choix d’utiliser ou non la mutabilité vous appartient et dépend de ce que vous estimez être le plus clair dans cette situation particulière.
Déclarer des constantes
Comme les variables immuables, les constantes sont des valeurs qui sont liées à un nom et ne peuvent pas être modifiées, mais il existe quelques différences entre les constantes et les variables.
Premièrement, vous ne pouvez pas utiliser mut avec les constantes. Les constantes ne sont pas simplement immuables par défaut — elles sont toujours immuables. Vous déclarez les constantes en utilisant le mot-clé const au lieu du mot-clé let, et le type de la valeur doit être annoté. Nous aborderons les types et les annotations de type dans la section suivante, [« Les types de données »][data-types], donc ne vous souciez pas des détails pour le moment. Sachez simplement que vous devez toujours annoter le type.
Les constantes peuvent être déclarées dans n’importe quelle portée, y compris la portée globale, ce qui les rend utiles pour les valeurs que de nombreuses parties du code doivent connaître.
La dernière différence est que les constantes ne peuvent être définies qu’avec une expression constante, et non avec le résultat d’une valeur qui ne pourrait être calculée qu’à l’exécution.
Voici un exemple de déclaration de constante :
#![allow(unused)]
fn main() {
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
}
Le nom de la constante est THREE_HOURS_IN_SECONDS, et sa valeur est définie comme le résultat de la multiplication de 60 (le nombre de secondes dans une minute) par 60 (le nombre de minutes dans une heure) par 3 (le nombre d’heures que nous voulons compter dans ce programme). La convention de nommage de Rust pour les constantes est d’utiliser des majuscules avec des tirets bas entre les mots. Le compilateur est capable d’évaluer un ensemble limité d’opérations à la compilation, ce qui nous permet de choisir d’écrire cette valeur d’une manière plus facile à comprendre et à vérifier, plutôt que de définir cette constante à la valeur 10 800. Consultez la [section de la référence Rust sur l’évaluation des constantes][const-eval] pour plus d’informations sur les opérations utilisables lors de la déclaration de constantes.
Les constantes sont valides pendant toute la durée d’exécution d’un programme, dans la portée dans laquelle elles ont été déclarées. Cette propriété rend les constantes utiles pour les valeurs de votre domaine applicatif que plusieurs parties du programme pourraient avoir besoin de connaître, comme le nombre maximum de points qu’un joueur peut gagner dans un jeu, ou la vitesse de la lumière.
Nommer les valeurs codées en dur utilisées dans votre programme sous forme de constantes est utile pour transmettre la signification de cette valeur aux futurs mainteneurs du code. Cela permet également de n’avoir qu’un seul endroit dans votre code à modifier si la valeur codée en dur devait être mise à jour à l’avenir.
Le masquage
Comme vous l’avez vu dans le tutoriel du jeu de devinettes au chapitre 2, vous pouvez déclarer une nouvelle variable portant le même nom qu’une variable précédente. Les Rustacés disent que la première variable est masquée par la seconde, ce qui signifie que c’est la seconde variable que le compilateur verra lorsque vous utiliserez le nom de la variable. En effet, la seconde variable masque la première, prenant toute utilisation du nom de variable pour elle-même jusqu’à ce qu’elle-même soit masquée ou que la portée se terminé. Nous pouvons masquer une variable en utilisant le même nom de variable et en répétant l’utilisation du mot-clé let comme suit :
Fichier : src/main.rs rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-03-associated-functions/src/main.rs:here}}
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x in the inner scope is: {x}");
}
println!("The value of x is: {x}");
}
Ce programme lie d’abord x à la valeur 5. Puis, il crée une nouvelle variable x en répétant let x =, prenant la valeur originale et ajoutant 1 de sorte que la valeur de x est 6. Ensuite, dans une portée intérieure créée avec les accolades, la troisième instruction let masque également x et crée une nouvelle variable, multipliant la valeur précédente par 2 pour donner à x une valeur de 12. Quand cette portée se terminé, le masquage intérieur prend fin et x redevient 6. Lorsque nous exécutons ce programme, il affichera ce qui suit : console {{#include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/output.txt}}
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/variables`
The value of x in the inner scope is: 12
The value of x is: 6
Le masquage est différent du fait de marquer une variable comme mut car nous obtiendrons une erreur à la compilation si nous essayons accidentellement de réassigner cette variable sans utiliser le mot-clé let. En utilisant let, nous pouvons effectuer quelques transformations sur une valeur tout en rendant la variable immuable une fois ces transformations terminées.
L’autre différence entre mut et le masquage est que, puisque nous créons effectivement une nouvelle variable lorsque nous utilisons à nouveau le mot-clé let, nous pouvons changer le type de la valeur tout en réutilisant le même nom. Par exemple, supposons que notre programme demande à un utilisateur de montrer combien d’espaces il souhaite entre du texte en saisissant des caractères espace, et que nous voulons ensuite stocker cette entrée sous forme de nombre : rust {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-04-shadowing-can-change-types/src/main.rs:here}}
fn main() {
let spaces = " ";
let spaces = spaces.len();
}
La première variable spaces est de type chaîne de caractères, et la seconde variable spaces est de type numérique. Le masquage nous évite ainsi de devoir trouver des noms différents, comme spaces_str et spaces_num ; nous pouvons plutôt réutiliser le nom plus simple spaces. Cependant, si nous essayons d’utiliser mut pour cela, comme montré ici, nous obtiendrons une erreur à la compilation : rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/src/main.rs:here}}
fn main() {
let mut spaces = " ";
spaces = spaces.len();
}
L’erreur indique que nous ne sommes pas autorisés à changer le type d’une variable : console {{#include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/output.txt}}
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
error[E0308]: mismatched types
--> src/main.rs:3:14
|
2 | let mut spaces = " ";
| ----- expected due to this value
3 | spaces = spaces.len();
| ^^^^^^^^^^^^ expected `&str`, found `usize`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` (bin "variables") due to 1 previous error
Maintenant que nous avons exploré le fonctionnement des variables, examinons les différents types de données qu’elles peuvent avoir.