Trait Drop pozwala określić niestandardową logikę czyszczenia zasobu przy wyjściu z zasięgu widoczności. Zwykle używa się go do zwalniania zasobów plikowych, sieciowych, zewnętrznych i niebezpiecznych. Drop jest wywoływany automatycznie przez kompilator.
Przykład:
struct FileWrapper { fd: i32, } impl Drop for FileWrapper { fn drop(&mut self) { println!("Zamykanie fd: {}", self.fd); unsafe { libc::close(self.fd); } } } fn main() { let file = FileWrapper { fd: 42 }; } // drop wywołany automatycznie
Cechy:
file.drop(), ale można wywołać przez std::mem::drop(file).panic!) wewnątrz drop — to prowadzi do "double panic" i awaryjnego zakończenia procesu.Pytanie: Co się stanie, jeśli podczas drop() struktury spróbujesz przekazać własność zasobu do innej części programu — na przykład, zwrócić go z drop?
Odpowiedź: Nie można "uratuować" ani przekazać wartości pola struktury w trakcie drop nikomu innemu poza samym dropem; próba zwrócenia lub przekazania wartości spowoduje błąd kompilacji lub naruszenie bezpieczeństwa pamięci (jeśli używasz unsafe). Przykład błędu:
impl Drop for MyStruct { fn drop(&mut self) -> T { // Błąd: Drop::drop nie może zwracać wartości! ... } }
Historia
W procesorze plików zapomniano zaimplementować Drop dla struktury, która bezpośrednio opakowuje surowy deskryptor pliku. Po setkach operacji pojawiał się "wyczerpany limit deskryptorów plików" — zasoby nie były zwalniane.
Historia
W bibliotece sieciowej zaimplementowano Drop dla opakowania nad połączeniem TCP, ale podczas drop wystąpił panic z powodu błędu zamknięcia gniazda. Prowadziło to do awaryjnego zakończenia wątku przy double panic (jedyny sposób — unikać panic w Drop!).
Historia
W systemie wtyczek próbowano zaimplementować czyszczenie za pomocą Drop, tworząc nowe zasoby podczas drop (np. logowanie do pliku). Prowadziło to do "already borrowed: BorrowMutError" z powodu rekurencyjnego wywołania drop dla powiązanych struktur i wycieków zasobów.