In C ++ gibt es drei Möglichkeiten, Parameter an eine Funktion zu übergeben: nach Wert, nach Wertreferenz und nach Wertreferenz. Von diesen führt die Übergabe eines Wertes zu einem Besitz in dem Sinne, dass die aufgerufene Funktion eine eigene Kopie erhält, und die Übergabe eines Wertverweises zeigt an, dass der Wert verbraucht werden kann, dh vom Aufrufer nicht mehr verwendet wird. Die Übergabe einer Wertreferenz bedeutet, dass das Objekt vorübergehend vom Anrufer ausgeliehen wird.
Diese sind jedoch in der Regel „konventionell“ und können vom Compiler nicht immer überprüft werden. Und Sie können mit aus Versehen eine l-Wert-Referenz in eine r-Wert-Referenz verwandelnstd::move()
. Konkret gibt es drei Probleme:
Eine Referenz kann das Objekt, auf das sie verweist, überleben. Das Lifetime-System von Rust verhindert dies.
Es kann mehr als eine veränderbare / nicht konstante Referenz gleichzeitig aktiv sein. Der Leihschein von Rust verhindert dies.
Sie können Referenzen nicht ablehnen. Sie können an einer Aufrufstelle nicht sehen, ob diese Funktion einen Verweis auf Ihr Objekt erstellt, ohne die Signatur der aufgerufenen Funktion zu kennen. Sie können Verweise daher nicht zuverlässig verhindern, indem Sie weder spezielle Methoden Ihrer Klassen löschen noch die Aufrufstelle auf Übereinstimmung mit einigen Styleguides ohne Verweise überprüfen.
Das Lebenszeitproblem betrifft die grundlegende Speichersicherheit. Es ist natürlich illegal, eine Referenz zu verwenden, wenn das referenzierte Objekt abgelaufen ist. Es ist jedoch sehr leicht, die Lebensdauer zu vergessen, wenn Sie eine Referenz in einem Objekt speichern, insbesondere wenn dieses Objekt den aktuellen Bereich überlebt. Das C ++ - Typensystem kann dies nicht berücksichtigen, da es die Objektlebensdauer überhaupt nicht modelliert.
Der std::weak_ptr
Smart Pointer codiert eine Besitzersemantik ähnlich einer einfachen Referenz, erfordert jedoch, dass das referenzierte Objekt über a verwaltet wirdshared_ptr
, dh referenziert wird. Dies ist keine kostenfreie Abstraktion.
Während C ++ über ein const-System verfügt, wird nicht verfolgt, ob ein Objekt geändert werden kann, sondern, ob ein Objekt über diese bestimmte Referenz geändert werden kann . Dies bietet keine ausreichenden Garantien für eine „furchtlose Parallelität“. Im Gegensatz dazu garantiert Rust, dass, wenn es eine aktive veränderbare Referenz gibt, die die einzige Referenz ist („Ich bin die einzige, die dieses Objekt ändern kann“), und wenn es nicht veränderbare Referenzen gibt, alle Referenzen auf das Objekt nicht veränderbar sind ("Während ich aus dem Objekt lesen kann, kann es niemand ändern").
In C ++ könnten Sie versucht sein, den Zugriff auf ein Objekt durch einen intelligenten Zeiger mit einem Mutex zu schützen. Wie oben bereits erwähnt, kann eine Referenz ihrer erwarteten Lebensdauer entgehen. Daher kann ein solcher intelligenter Zeiger nicht garantieren, dass er der einzige Zugriffspunkt auf sein verwaltetes Objekt ist. Ein solches Schema mag in der Praxis tatsächlich funktionieren, weil die meisten Programmierer sich nicht selbst sabotieren wollen, aber aus Sicht des Typsystems ist dies immer noch völlig unklar.
Das allgemeine Problem bei intelligenten Zeigern besteht darin, dass sie Bibliotheken über der Kernsprache sind. Der Satz von Kernsprachfunktionen ermöglicht diese intelligenten Zeiger, die z. B. std::unique_ptr
Bewegungskonstruktoren benötigen. Sie können jedoch keine Mängel innerhalb der Kernsprache beheben. Die Fähigkeit, beim Aufrufen einer Funktion implizit Referenzen zu erstellen und zusammenhängende Referenzen zu haben, bedeutet, dass die Kernsprache von C ++ nicht in Ordnung ist. Die Unmöglichkeit, veränderbare Verweise auf einen einzigen zu beschränken, bedeutet, dass C ++ keine Garantie für die Sicherheit gegen Rennbedingungen bei jeglicher Art von Parallelität geben kann.
Natürlich sind C ++ und Rust in vielerlei Hinsicht mehr gleich als ungleich, insbesondere in Bezug auf ihre Konzepte statisch bestimmter Objektlebensdauern. Während es möglich ist , korrekte C ++ - Programme zu schreiben (vorausgesetzt, keiner der Programmierer macht Fehler), garantiert Rust die Richtigkeit der besprochenen Eigenschaften.