November 24, 20254 min

Comprendre le Trait Drop en Rust

m
mayo

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 drop quand :
    • 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

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 drop directement ; utilise std::mem::drop pour 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 Drop ne peuvent pas être Copy.

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 Drop si le cleanup est géré par d'autres types (ex : Box, Vec).
  • Thread-Safety : Utilise Arc + Mutex au lieu de locking manuel dans drop.

Points Clés

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é

Expérimente : Que se passe-t-il si tu appelles mem::forget sur un type avec Drop ?

Réponse : Le destructeur ne s'exécutera pas, causant potentiellement une fuite de ressource (ex : fichiers non fermés ou mémoire non libérée).

Retour au blog
Partager ::