Ich implementiere ein Objekt, das mehrere Ressourcen besitzt, die von C-Bibliotheken über FFI erstellt wurden. Um zu bereinigen, was bereits gemacht wurde, wenn der Konstruktor in Panik gerät, wickle ich jede Ressource in eine eigene Struktur und implementiere Drop
für sie. Wenn es jedoch zum Löschen des Objekts selbst kommt, kann ich nicht garantieren, dass Ressourcen in einer sicheren Reihenfolge abgelegt werden, da Rust die Reihenfolge der Felder einer Struktur nicht definiert.
Normalerweise würden Sie dies lösen, indem Sie es so einrichten, dass das Objekt nicht die Ressourcen besitzt, sondern es ausborgt (damit sich die Ressourcen gegenseitig ausborgen). Im Endeffekt schiebt sich das Problem auf den aufrufenden Code zu, wo die Drop-Reihenfolge gut definiert ist und mit der Semantik des Ausleihens durchgesetzt wird. Aber das ist unpassend für meinen Anwendungsfall und im Allgemeinen ein bisschen Ausreden.
Was ärgerlich ist, ist, dass dies unglaublich einfach wäre, wenn drop
aus irgendeinem Grund self
anstelle von &mut self
verwendet hätte. Dann könnte ich einfach std::mem::drop
in meiner gewünschten Reihenfolge aufrufen.
Gibt es eine Möglichkeit, dies zu tun? Wenn nicht, gibt es eine Möglichkeit, im Falle einer Panik des Konstrukteurs aufzuräumen, ohne sie manuell zu erfassen und erneut zu bearbeiten?
Sie können die Reihenfolge der Reihenfolge Ihrer Strukturfelder auf zwei Arten festlegen:
Ich schrieb RFC 1857 , um die Reihenfolge anzugeben und es wurde am 03.07.2017 verschmolzen! Nach dem RFC werden Strukturfelder in derselben Reihenfolge abgelegt, wie sie deklariert sind.
Sie können dies überprüfen, indem Sie das Beispiel unten
ausführen %Vor%Die Ausgabe sollte lauten:
%Vor% RFC 1860 führt das ManuallyDrop
Typ, der einen anderen Typ umschließt und dessen Destruktor deaktiviert. Die Idee ist, dass Sie das Objekt manuell löschen können, indem Sie eine spezielle Funktion aufrufen ( ManuallyDrop::drop
). Diese Funktion ist nicht sicher, da der Speicher nach dem Löschen des Objekts nicht initialisiert ist.
Sie können ManuallyDrop
verwenden, um die Reihenfolge der Felder Ihrer Felder im Destruktor Ihres Typs explizit anzugeben:
Wenn Sie dieses Verhalten benötigen, ohne eine der neueren Methoden verwenden zu können, lesen Sie weiter ...
Die Methode drop
kann ihren Parameter nicht als Wert annehmen, da der Parameter am Ende des Bereichs wieder fallen würde. Dies würde zu einer unendlichen Rekursion für alle Destruktoren der Sprache führen.
Ein Muster, das ich in einigen Codebasen gesehen habe, besteht darin, die Werte, die in Option<T>
fallengelassen werden, zu umbrechen. Anschließend können Sie im Destruktor jede Option durch None
ersetzen und den resultierenden Wert in der richtigen Reihenfolge löschen.
Zum Beispiel in der Scoped-threadpool -Kiste Das Pool
-Objekt enthält Threads und einen Sender, der neue Arbeit plant. Um die Threads beim Ablegen korrekt zu verbinden, sollte zuerst der Absender und dann der Thread fallengelassen werden.
Natürlich ist es eher eine Umgehung als eine richtige Lösung, die Dinge auf diese Weise zu erledigen. Wenn das Optimierungsprogramm nicht beweisen kann, dass die Option immer Some
ist, haben Sie jetzt eine zusätzliche Verzweigung für jeden Zugriff auf Ihr Strukturfeld.
Glücklicherweise verhindert nichts, dass eine zukünftige Version von Rust ein Feature implementiert, das die Festlegung der Reihenfolge ermöglicht. Es würde wahrscheinlich einen RFC erfordern, scheint aber durchaus machbar. Es gibt eine fortlaufende Diskussion über den Issue Tracker zum Festlegen der Drop-Reihenfolge für die Sprache, obwohl diese inaktiv war letzten Monaten.
Wenn das Zerstören Ihrer Strukturen in der falschen Reihenfolge unsicher ist, sollten Sie in Betracht ziehen, ihre Konstruktoren unsafe
zu erstellen und diese Tatsache zu dokumentieren (falls Sie das nicht bereits getan haben). Andernfalls wäre es möglich, unsicheres Verhalten nur durch Erstellen der Strukturen auszulösen und sie außerhalb des Gültigkeitsbereichs fallen zu lassen.