ProgrammationDéveloppeur Rust, Data Engineer

Parlez-nous de la manière dont les tableaux dynamiques (Vec) sont implémentés en Rust et de leur allocation. Quelles difficultés peuvent survenir lors de l'ajout/suppression d'éléments, et comment éviter les allocations inutiles?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Vec<T> est un tableau dynamique, extensible, qui stocke les éléments dans un seul bloc de mémoire alloué (sur le tas). L'ajout d'un nouvel élément (push) augmente la longueur, et si nécessaire, une nouvelle mémoire est allouée (réallocation). Lors du push, la capacité est augmentée exponentiellement pour éviter des reallocations constantes. La capacité ne diminue pas automatiquement lors de la suppression d'un élément (pop/remove).

Un problème fréquent est les allocations excessives et les reallocations lors d'ajouts constants.

Exemple de travail avec allocation préalable:

let mut v = Vec::with_capacity(1000); for i in 0..1000 { v.push(i); } assert_eq!(v.capacity(), 1000);

Question piégeuse

Question : Que se passera-t-il avec la capacité de vec après l'appel à v.shrink_to_fit() ? Sera-t-elle exactement égale à la longueur ?

Mauvaise réponse : Oui, toujours, après shrink_to_fit capacity == len.

Bonne réponse : Pas nécessairement, l'implémentation de shrink_to_fit est un "souhait" pour l'allocateur. En général, elle tend vers la capacité minimale possible, des particularités peuvent exister selon l'implémentation de l'allocateur (par exemple, elle peut rester supérieure à la longueur).

Exemple :

let mut v = Vec::with_capacity(10); for i in 0..5 { v.push(i); } v.shrink_to_fit(); // capacity ≥ len (5), mais pas garanti qu'elle soit == len

Exemples d'erreurs réelles dues à la méconnaissance des subtilités du sujet


Histoire

Un développeur a multiplié les push d'objets dans Vec sans définir la capacité, ce qui entraînait une croissance exponentielle des reallocations avec de grands volumes de données, ralentissant l'ensemble du traitement dans des "boucles lourdes". L'optimisation avec with_capacity a réduit le temps par 10.


Histoire

L'équipe a tenté d'économiser de la mémoire en appelant régulièrement shrink_to_fit après chaque pop(). Cela a finalement entraîné des boucles de reallocations/libérations constantes et une dégradation des performances, frôlant le service avec un DoS.


Histoire

Ils ont laissé Vec comme champ d'une structure avec des références internes à ses éléments. Après réallocation (push au-delà de la capacité), les références ont été invalidées — les bugs qui en ont résulté étaient difficiles à détecter jusqu'au déploiement en production.