backend / rust
Variables et mutabilité
Explication
Ce que vous allez apprendre
- Comprendre pourquoi une variable Rust est immuable par défaut
- Rendre une variable mutable explicitement avec
mut - Utiliser le shadowing pour redéclarer une variable avec un nouveau type ou une nouvelle valeur
- Distinguer une constante (
const) d'une variable immuable simple (let) - Comprendre la différence entre muter une référence et muter la valeur pointée
Dans quel contexte ?
Un développeur qui vient de Python ou JavaScript écrit let x = 5; puis tente, quelques lignes plus loin, de faire x = 6; — et le compilateur Rust refuse catégoriquement de compiler, avec un message explicite : "cannot assign twice to immutable variable". Ce refus, qui surprend au début, est en réalité l'un des piliers de la sécurité de Rust : il force à déclarer intentionnellement quelles variables peuvent réellement changer.
D'abord, il faut assimiler ce renversement de philosophie
Dans la quasi-totalité des langages, une variable est mutable par défaut, et il faut un mot-clé spécial (const, final, readonly) pour la rendre immuable. Rust fait exactement l'inverse : let x = 5 crée une variable immuable, et il faut explicitement écrire let mut y = 5 pour autoriser sa modification ultérieure.
Ce choix n'est pas arbitraire : en pratique, la grande majorité des variables dans un programme ne sont jamais réassignées après leur création. Rendre l'immuabilité par défaut pousse à documenter intentionnellement les rares cas qui ont réellement besoin de changer.
Une fois cette base acquise, un mécanisme surprenant complète le tableau : le shadowing
Le "shadowing" permet de redéclarer une variable avec le même nom via un nouveau let, créant en réalité une toute nouvelle variable qui masque l'ancienne. Contrairement à mut, le shadowing autorise même de changer le type de la donnée au passage.
| Mécanisme | Syntaxe | Ce qu'il permet |
|---|---|---|
let simple | let x = 5; | Variable immuable, un seul type, jamais réassignée |
let mut | let mut y = 5; y = 6; | Variable mutable, même type, réassignable |
| Shadowing | let z = 5; let z = format!("{}", z); | Nouvelle variable, type potentiellement différent |
const | const MAX: u32 = 100; | Toujours immuable, type obligatoire, valeur figée à la compilation |
Prérequis
Aucune connaissance préalable de Rust n'est nécessaire au-delà de la première leçon (installation et premier programme).
Il reste une nuance subtile entre mut sur une référence et mut sur la valeur
let mut compteur = 0; rend compteur mutable. let reference = &mut compteur; crée ensuite une référence mutable exclusive vers cette variable : *reference += 1 modifie alors la valeur pointée, pas la référence elle-même. Cette distinction entre "la référence peut changer où elle pointe" et "on peut modifier ce qui est pointé" deviendra centrale dans la prochaine leçon sur le borrowing.
Piège fréquent
Une variable rendue mutable avec mut mais jamais réellement modifiée déclenche un avertissement du compilateur ("variable does not need to be mutable"). Ce n'est pas une erreur bloquante, mais un signal fort que ton code contient une intention mal exprimée — retire le mut inutile dès que ce warning apparaît.
Une bonne pratique à adopter dès maintenant
Bonne pratique
Laisse toujours let (sans mut) comme choix par défaut, et n'ajoute mut qu'au moment précis où le compilateur te force à le faire. Cette discipline, qui semble contraignante au début, documente immédiatement l'intention de chaque variable pour quiconque relit ton code plus tard.
Maintenant que tu maîtrises la mutabilité de base, la prochaine leçon explore les types fondamentaux de Rust en détail, avant d'aborder le concept qui distingue vraiment ce langage de tous les autres : l'ownership.
Commandes & code
Variables et mutabilité
En Rust, tout est immuable par défaut : la mutabilité doit être explicitement demandée.
fn main() {
// Immuable par defaut : erreur de compilation si on tente de reassigner
let x = 5;
// x = 6; // ERREUR: cannot assign twice to immutable variable
// mut rend la variable mutable
let mut y = 5;
y = 6;
println!("{}", y);
// Shadowing : redeclarer le meme nom avec `let` cree une NOUVELLE variable
let z = 5;
let z = z * 2; // z vaut maintenant 10, type potentiellement different
let z = format!("valeur: {}", z); // z devient une String ici
println!("{}", z);
// Constantes : toujours immuables, type obligatoire, evaluees a la compilation
const MAX_POINTS: u32 = 100_000;
println!("{}", MAX_POINTS);
// Portee (scope) : une variable vit jusqu'a la fin de son bloc
let a = 10;
{
let a = 20; // masque le `a` externe dans ce bloc
println!("interieur: {}", a);
}
println!("exterieur: {}", a);
// mut sur une reference vs mut sur la valeur pointee : nuance importante
let mut compteur = 0;
let reference = &mut compteur; // reference mutable exclusive
*reference += 1;
println!("{}", compteur);
}Résumé
let= immuable par défaut,let mut= mutable : un choix explicite et volontaire.- Le shadowing permet de réutiliser un nom avec un type ou une valeur différente sans
mut. - Une constante (
const) est toujours immuable et doit avoir un type explicite. - Ce choix "immuable par défaut" est une des bases de la sécurité mémoire de Rust.
Exercices pratiques
Mission : corriger un compteur qui refuse de compiler
Objectif : Diagnostiquer une erreur de mutabilite dans un code fourni et choisir entre mut, shadowing et const selon le bon usage.
Contexte
Un collegue t'envoie un extrait de code qui ne compile pas :
fn main() {
let compteur = 0;
compteur = compteur + 1;
let limite = 100;
limite = limite + 1;
println!("{} / {}", compteur, limite);
}Il pense avoir trouve un bug du compilateur. Ta mission : identifier precisement ce qui bloque, et proposer la correction la plus idiomatique possible ligne par ligne.