ProgrammationDéveloppeur Backend

Comment fonctionne la collection HashMap en Rust ? Quelles subtilités sont liées à la possession des clés et des valeurs, à la modification et à l'extraction des données, ainsi qu'à la sécurité de l'accès concurrent ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Histoire de la question :

En Rust, l'une des collections les plus souvent utilisées est HashMap — un tableau associatif implémenté par une table de hachage. La particularité de l'implémentation en Rust est le respect strict des règles de possession et de sécurité mémoire, ainsi que la sécurité des threads uniquement dans certaines conditions.

Problème :

Contrairement à d'autres langages avec collecte des ordures, en Rust, toutes les opérations sur HashMap (ajout, extraction, modification) nécessitent de respecter les règles de possession. Par exemple, vous ne pouvez pas modifier la collection tout en ayant des références actives à son contenu. Il y a aussi la question de la possession lors de l'insertion : les éléments sont soit déplacés, soit clonés. Et lors d'un accès concurrent, il y a un risque d'accès concurrent aux données.

Solution :

  • Les clés et les valeurs sont déplacées dans HashMap lors de l'insertion (ou copiées si elles implémentent Copy), restant sous la gestion de la collection.
  • L'extraction par clé renvoie une référence ou une Option, pour un accès mutable — via get_mut ou entry API.
  • Pour un accès concurrent sécurisé, HashMap doit être placé dans des wrappers synchronisés (Mutex, RwLock) ou utiliser des hashmaps concurrentes spéciales à partir de crates.

Exemple de code :

use std::collections::HashMap; fn main() { let mut map = HashMap::new(); map.insert("key", 10); if let Some(value) = map.get_mut("key") { *value += 1; } println!("{:?}", map.get("key")); }

Caractéristiques clés :

  • HashMap nécessite le transfert de possession des éléments, ce qui influence la durée de vie et la mutabilité.
  • Lors de la modification ou de l'extraction d'une valeur via get_mut, vous ne pouvez pas modifier la structure même de la carte (insérer ou supprimer des clés).
  • Ce n'est pas thread-safe par défaut, une synchronisation supplémentaire est nécessaire.

Questions pièges.

L'accès simultané à plusieurs éléments de HashMap par différentes références est-il possible ?

Non, Rust ne le permet pas via l'API standard : l'itération avec modification n'est possible qu'avec une référence exclusive à l'ensemble de HashMap.

Que se passe-t-il si vous essayez d'obtenir une référence mutable et une référence immuable sur le même élément ?

Le compilateur générera une erreur pour violation des règles du vérificateur de prêts : il n'est pas possible de combiner un emprunt mutable et un emprunt immuable d'une même valeur.

L'API entry() fonctionne-t-elle uniquement pour l'insertion de nouveaux éléments ?

Non, via l'API Entry, vous pouvez également accéder à la modification d'une valeur existante, et pas seulement à l'insertion.

map.entry("key").and_modify(|v| *v += 1).or_insert(0);

Erreurs typiques et anti-modèles

  • Stocker des références aux valeurs de HashMap en dehors de la durée de vie de la carte, ce qui mène à des références pendantes.
  • Modification et lecture simultanées sans Mutex ou RwLock dans un environnement multithread.
  • Utilisation incorrecte de l'API Entry uniquement comme alternative à insert, et non comme moyen de travail atomique avec le contenu.

Exemple de la vie réelle

Cas négatif

Fournir des références aux valeurs de HashMap dans des variables globales sans garantie de durée de vie de la carte.

Avantages :

  • Accès rapide.

Inconvénients :

  • Références pendantes, UB, débogage compliqué des bugs.

Cas positif

Envelopper HashMap dans Arc<Mutex<_>> pour une utilisation à partir de plusieurs threads.

Avantages :

  • Accès sécurisé et modification de la collection à partir de différents threads.

Inconvénients :

  • Problème de performance en raison des blocages en cas de forte concurrence.