October 29, 20256 min

Vec::drain() vs Vec::truncate() ou Vec::clear() ?

m
mayo

Qu'est-ce que Vec::drain() ?

drain() supprime une plage d'éléments d'un Vec tout en cédant leur ownership via un itérateur. Contrairement à truncate() ou clear(), il vous permet de traiter les éléments supprimés avant qu'ils ne soient libérés.

vec!['a','b','c','d'] — trois façons de supprimer des éléments ['a','b','c','d'] drain(1..3) truncate(2) clear() ['a','d'] ['a','b'] [] cède 'b','c' via itérateur libère la fin, sans itérateur libère tout, sans itérateur Les trois préservent la capacité du Vec — seul drain() permet d'utiliser les valeurs supprimées

Signature

pub fn drain<R>(&mut self, range: R) -> Drain<'_, T>
where
    R: RangeBounds<usize>,

Caractéristiques principales

Méthode Supprime les éléments Cède l'ownership Préserve la capacité Complexité temporelle
drain(..) Oui ✅ Oui (via itérateur) ✅ Oui O(n)
truncate() Oui (à partir d'un index) ❌ Non ✅ Oui O(1)
clear() Tous ❌ Non ✅ Oui O(1)

Quand utiliser chaque méthode

1. Vec::drain()

Cas d'usage : Traiter les éléments supprimés (par exemple, filtrer, transformer ou supprimer par lots).

Exemple :

let mut vec = vec!['a', 'b', 'c', 'd'];
for ch in vec.drain(1..3) {  // Supprime 'b' et 'c'
    println!("Supprimé : {}", ch);  // Affiche 'b', puis 'c'
}
assert_eq!(vec, ['a', 'd']);  // Garde les éléments restants

Performance : Évite les allocations supplémentaires si on réutilise l'itérateur.

2. Vec::truncate()

Cas d'usage : Supprimer rapidement des éléments depuis la fin sans les traiter.

Exemple :

let mut vec = vec![1, 2, 3, 4];
vec.truncate(2);  // Libère 3 et 4 (pas d'itérateur)
assert_eq!(vec, [1, 2]);

3. Vec::clear()

Cas d'usage : Supprimer tous les éléments (plus rapide que drain(..) si vous n'en avez pas besoin).

Exemple :

let mut vec = vec![1, 2, 3];
vec.clear();  // Libère tous les éléments
assert!(vec.is_empty());

Comportement mémoire

  • Les trois méthodes conservent la capacité du Vec (pas de réallocation si des éléments sont rajoutés).
  • drain() est paresseux : les éléments ne sont libérés que lorsque l'itérateur est consommé.

Cette paresse signifie que le travail intéressant s'étale sur la durée de vie de la garde Drain, et non dans l'appel à drain() :

Durée de vie de la garde Drain issue de vec.drain(1..3) 1 · appel de drain(1..3) len passe à 1 aussitôt, &mut vec pris par la garde 2 · next() cède 'b', 'c' la propriété vous revient, un élément à la fois 3 · garde détruite tout ce qui n'a pas été cédé est libéré ici même 4 · queue restaurée 'd' décalé, vec devient ['a','d'], capacité gardée Les étapes 3 et 4 viennent de l'impl Drop de Drain : la suppression a lieu même sans aucun appel à next(). Tant que la garde vit, vec est emprunté mutablement et intouchable.

Utilisation avancée : Réutiliser le stockage

drain() est idéal pour remplacer efficacement un sous-ensemble d'éléments :

let mut vec = vec!["old", "old", "new", "old"];
vec.drain(0..2).for_each(drop);  // Supprime les deux premiers
vec.insert(0, "fresh");
assert_eq!(vec, ["fresh", "new", "old"]);

Points clés à retenir

  • drain() : À utiliser quand vous devez traiter les éléments supprimés ou supprimer par lots.
  • truncate()/clear() : À utiliser pour une suppression en masse rapide sans traitement.
  • 🚀 Tous préservent la capacité : Pas de surcoût de réallocation pour les opérations futures.

Exemple concret

Dans un moteur de jeu, drain() pourrait efficacement supprimer les entités expirées tout en permettant une logique de nettoyage (par exemple, sauvegarder l'état).

Essayez ceci : Que se passe-t-il si vous utilisez drain() mais ne consommez pas l'itérateur ?

Réponse : Les éléments sont quand même supprimés lorsque l'itérateur Drain est libéré (grâce à son implémentation de Drop).

Retour au blog
Partager ::