Aller au contenu

Les Hashmap en Rust : des dictionnaires

Comprendre les HashMap en Rust

En Rust, les HashMap sont l’une des structures de données les plus couramment utilisées pour stocker des paires clé-valeur. Elles sont particulièrement utiles quand tu as besoin de rechercher des valeurs à partir de clés de manière efficace. Cet article va t’expliquer comment fonctionnent les HashMap, comment les utiliser, et quelques points à prendre en compte pour bien les maîtriser.


Qu’est-ce qu’une HashMap ?

Une HashMap en Rust est une collection qui stocke des paires clé-valeur. Chaque clé est associée à une valeur, et tu peux retrouver rapidement une valeur à partir de sa clé. C’est une implémentation classique de ce qu’on appelle une table de hachage.

En Rust, la HashMap est définie dans le module std::collections . Tu peux l’utiliser en l’important avec cette ligne :

use std::collections::HashMap;

Créer une HashMap

Pour créer une HashMap vide, tu utilises la fonction HashMap::new() :

use std::collections::HashMap;
fn main() {
let mut
scores = HashMap::new();
scores.insert(String::from("Bleu"), 10);
scores.insert(String::from("Rouge"), 50);
println!("{:?}", scores);
}

Ici, on crée une HashMap scores qui associe des noms d’équipes à des scores. Tu remarqueras qu’on utilise la méthode insert() pour ajouter des paires clé-valeur dans la HashMap.

Autre façon de créer une HashMap

Tu peux aussi créer une HashMap à partir de deux vecteurs, un pour les clés et un pour les valeurs, avec la méthode collect() :

fn main() {
let equipes = vec![String::from("Bleu"), String::from("Rouge")];
let scores = vec![10, 50];
let mut
map: HashMap<_, _> = equipes.into_iter().zip(scores.into_iter()).collect();
println!("{:?}", map);
}

Ici, zip() permet d’associer les éléments des deux vecteurs en paires clé-valeur.


Accéder à des valeurs dans une HashMap

Pour accéder à une valeur dans une HashMap, tu peux utiliser la méthode get() en fournissant la clé :

fn main() {
let mut
scores = HashMap::new();
scores.insert(String::from("Bleu"), 10);
scores.insert(String::from("Rouge"), 50);
let score_bleu = scores.get("Bleu");
match score_bleu {
Some(score) => println!("Score de l'équipe Bleue : {}", score),
None => println!("Pas de score trouvé."),
}
}

La méthode get() retourne une Option parce qu’il est possible que la clé ne soit pas présente dans la HashMap. Tu dois donc gérer le cas où la clé n’existe pas.


Itérer sur une HashMap

Tu peux facilement itérer sur une HashMap avec une boucle for . Chaque itération te fournit une référence à une paire clé-valeur :

fn main() {
let mut
scores = HashMap::new();
scores.insert(String::from("Bleu"), 10);
scores.insert(String::from("Rouge"), 50);
for (cle, valeur) in &scores {
println!("Équipe : {}, Score : {}", cle, valeur);
}
}

Remplacer une valeur

Si tu ajoutes une nouvelle valeur pour une clé qui existe déjà, la nouvelle valeur remplace simplement l’ancienne :

fn main() {
let mut
scores = HashMap::new();
scores.insert(String::from("Bleu"), 10);
scores.insert(String::from("Bleu"), 25); // Remplace le 10 par 25
println!("{:?}", scores); // Affiche {"Bleu": 25}
}

Insérer une valeur uniquement si la clé n’existe pas

Parfois, tu veux insérer une valeur uniquement si la clé n’existe pas encore. Pour cela, Rust fournit la méthode entry() et or_insert() :

fn main() {
let mut
scores = HashMap::new();
scores.insert(String::from("Bleu"), 10);
scores.entry(String::from("Rouge")).or_insert(50); // Insère 50 pour "Rouge"
scores.entry(String::from("Bleu")).or_insert(50); // Ne change rien pour "Bleu"
println!("{:?}", scores);
}

Dans cet exemple, une valeur est insérée pour la clé "Rouge" car elle n’existait pas encore, mais la valeur pour "Bleu" reste inchangée car elle était déjà présente.


Mise à jour d’une valeur basée sur l’ancienne

Rust te permet aussi de mettre à jour une valeur en fonction de sa valeur actuelle. Par exemple, si tu veux augmenter le score d’une équipe, tu peux faire ceci :

fn main() {
let mut
scores = HashMap::new();
scores.insert(String::from("Bleu"), 10);
let score = scores.entry(String::from("Bleu")).or_insert(0);
*score += 10;
println!("{:?}", scores); // Affiche {"Bleu": 20}
}

Ici, la méthode entry() te donne un accès mutable à la valeur associée à "Bleu", et tu peux la modifier directement.


HashMap et propriété

Lorsque tu insères des données dans une HashMap, Rust déplace les valeurs, c’est-à-dire qu’elles ne sont plus accessibles sous leur forme originale après insertion. Voici un exemple pour illustrer cela :

fn main() {
let nom = String::from("Bleu");
let score = 10;
let mut
scores = HashMap::new();
scores.insert(nom, score);
// println!("{}", nom); // Erreur : `nom` a été déplacé
}

Dans cet exemple, après avoir inséré nom dans la HashMap, tu ne peux plus utiliser la variable nom parce qu’elle a été déplacée. Cela s’applique aussi bien aux clés qu’aux valeurs si elles possèdent une sémantique de possession.

Pour contourner cela, tu peux utiliser des références (&) ou des types clonables comme Copy pour les types scalaires.


Conclusion

Les HashMap en Rust sont une structure de données puissante et flexible pour gérer des paires clé-valeur. Elles permettent des recherches rapides et offrent plusieurs méthodes utiles pour insérer, accéder et mettre à jour des données. Cependant, il faut être attentif à la gestion de la propriété lors de l’insertion de données dans une HashMap, car Rust suit ses règles strictes de possession.

Que tu aies besoin de stocker des scores, des configurations ou tout autre ensemble de données associées, les HashMap sont un excellent outil à avoir dans ta boîte à outils Rust.