Comprendre le Trait Drop en Rust
Table des matières
Le trait Drop en Rust permet une logique de cleanup personnalisée quand une valeur sort du scope, fournissant une gestion déterministe des ressources similaire au RAII de C++ (Resource Acquisition Is Initialization). Il assure la memory safety et la désallocation appropriée des ressources sans garbage collector.
Qu'est-ce que le Trait Drop ?
Le trait Drop définit une seule méthode, drop, qui est automatiquement appelée quand une valeur est détruite :
trait Drop {
fn drop(&mut self); // Appelée automatiquement quand la valeur est détruite
}
Comment ça Fonctionne
- Invocation Automatique : Rust appelle
dropquand :- Une variable sort du scope.
- L'ownership est transférée (ex : moved dans une fonction).
- Explicitement droppée via
std::mem::drop.
- Ordre LIFO : Les valeurs sont droppées dans l'ordre inverse de leur déclaration (comportement stack-like).
Exemple : Drop Basique :
struct Resource {
id: u32,
}
impl Drop for Resource {
fn drop(&mut self) {
println!("Dropping resource {}", self.id);
}
}
fn main() {
let _res1 = Resource { id: 1 }; // Droppée en second
let _res2 = Resource { id: 2 }; // Droppée en premier
}
Sortie :
Dropping resource 2
Dropping resource 1
Trois événements différents convergent vers le même appel au destructeur — et exactement un le contourne :
Quand implémenter Drop manuellement
1. Cleanup de Ressources
Pour gérer des ressources non-mémoire comme fichiers, sockets, ou locks :
struct DatabaseConnection {
// Détails de connexion
}
impl Drop for DatabaseConnection {
fn drop(&mut self) {
self.close(); // Assure que la connexion est libérée
}
}
2. Gestion mémoire Personnalisée
Pour intégrer avec FFI ou code unsafe :
struct RawBuffer {
ptr: *mut u8,
}
impl Drop for RawBuffer {
fn drop(&mut self) {
unsafe { libc::free(self.ptr as *mut _); } // Libère manuellement mémoire heap
}
}
3. Logging/Télémétrie
Pour tracker le cycle de vie d'objets :
struct MetricsTracker {
start: std::time::Instant,
}
impl Drop for MetricsTracker {
fn drop(&mut self) {
log::info!("Tracker dropped after {}ms", self.start.elapsed().as_millis());
}
}
Règles clés
- Pas d'Appels Explicites : Appelle rarement
dropdirectement ; utilisestd::mem::droppour explicitement drop une valeur. - Pas de Panics : Évite de paniquer dans
drop, car cela peut mener à des double-drops ou arrêts de programme. - Auto Traits : Les types implémentant
Dropne peuvent pas êtreCopy.
Drop vs. Copy/Clone
| Trait | But | Mutuellement Exclusif ? |
|---|---|---|
Drop |
Logique de cleanup | Oui (ne peut pas être Copy) |
Copy |
Copie bitwise | Oui |
Clone |
Deep copy explicite | Non |
Avancé : #[may_dangle] (Nightly)
Pour les types génériques où T pourrait ne pas avoir besoin d'être droppé (unsafe) :
unsafe impl<#[may_dangle] T> Drop for MyBox<T> {
fn drop(&mut self) { /* ... */ }
}
Quand ne pas utiliser Drop
- Données Simples : Pas besoin de
Dropsi le cleanup est géré par d'autres types (ex :Box,Vec). - Thread-Safety : Utilise
Arc+Mutexau lieu de locking manuel dansdrop.
Drop, en résumé
Utilise Drop pour :
- Cleanup de ressources (fichiers, locks, mémoire).
- Garanties FFI/safety-critical.
- Debugging/profiling.
Évite :
- Réimplémenter de la logique fournie par Rust (ex : désallocation de
Box). - Opérations complexes qui pourraient paniquer.
Exemple Réel : Le type MutexGuard utilise Drop pour libérer les locks automatiquement :
{
let guard = mutex.lock(); // Lock acquis
// ...
} // `guard` dropped ici → lock libéré
mem::forget sur un type qui implémente Drop saute complètement le destructeur. C'est du Rust
safe — fuir n'est pas de l'unsoundness — mais ça veut dire un fichier non fermé ou un buffer non
libéré, donc c'est un outil délibéré et non une échappatoire.
À lire ensuite
- Étape 18 sur 59•6 minRust Vec::new() vs. with_capacity() : Quand utiliser chacune
Vec::new() alloue à la première insertion, with_capacity(n) alloue une fois pour n éléments. Quand la différence compte vraiment.
- Étape 19 sur 59•5 minQuelle est la différence entre Box<[T]> et Vec<T> ?
- Étape 20 sur 59•7 minQuelles sont les différences entre into_iter(), iter() et iter_mut() ?