ist es möglich, Rusts Eigentumsmodell mit einem generischen C ++ - Wrapper zu erreichen?

15

Durchsuchen Sie diesen Artikel über die Sicherheit von Rusts Parallelität:

http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html

Ich habe mich gefragt, wie viele dieser Ideen in C ++ 11 (oder neuer) erreicht werden können. Kann ich insbesondere eine Besitzerklasse erstellen, die den Besitz auf eine beliebige Methode überträgt, an die er übergeben werden kann? Es scheint, dass C ++ so viele Möglichkeiten hat, Variablen zu übergeben, dass es unmöglich wäre, aber vielleicht könnte ich die Klasse oder das Template einschränken, um sicherzustellen, dass bei jedem Methodenübergang ein gewisser Template-Code ausgeführt wird?

Brannon
quelle
Einige Zitate aus dem Link würden diese Frage verbessern
Martin Ba
2
@delnan (Safe) Rust garantiert, dass Sie nie mehr als einen veränderlichen Verweis auf etwas gleichzeitig haben und dass Sie nie einen veränderlichen Verweis auf etwas haben, auf das Sie auch schreibgeschützte Verweise haben. Es gibt auch einige Einschränkungen beim Übertragen von Daten zwischen Threads. Zusammen verhindern diese eine signifikante Klasse von Threading-bezogenen Fehlern und erleichtern das Überlegen des Status von Objekten, selbst in Code mit einem einzigen Thread.
CodesInChaos
3
Sie glauben nicht, dass Sie das Ausleihen so ausdrücken können, wie es der C ++ - Compiler überprüfen könnte, sodass Sie mit dem zugehörigen Leistungstreffer auf die Laufzeiterzwingung zurückgreifen müssen.
CodesInChaos
1
Sind die Gültigkeitsbereiche in C ++ 11 nicht bereits durch intelligente Zeiger implementiert?
Akshat Mahajan
1
@JerryJeremiah Rust hat eine Vielzahl von Referenztypen. Die grundlegenden &erfordern keine Art von Werbung, um verwendet zu werden. Wenn Sie versuchen, für eine &mutWeile einen anderen (veränderlichen oder nicht veränderlichen) Verweis auf dasselbe Element zu erhalten, können Sie nicht kompilieren. RefCell<T>Verschiebt der Scheck die Laufzeit, so kommt es zu einer Panik, wenn Sie versuchen, .borrow_mut()etwas zu tun , das bereits ein aktives .borrow()oder ein aktives hat .borrow_mut(). Rust hat auch Rc<T>(shared owning pointer) und seine Geschwister Weak<T>, aber es geht um Eigentum, nicht um Veränderbarkeit. Kleben Sie einen RefCell<T>in sie für die Veränderbarkeit.
8bittree

Antworten:

8

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_ptrSmart 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_ptrBewegungskonstruktoren 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.

amon
quelle
Wenn das Problem darin besteht, dass C ++ die Eigentümerschaft in der Kernsprache nicht nachverfolgt, ist es dann möglich, diese Funktionalität durch Metaprogrammierung zu implementieren? Das heißt, Sie würden eine neue Klasse für intelligente Zeiger erstellen, die speichersicher wäre, indem Sie (1) sie zwingen, ausschließlich auf Objekte zu verweisen, die nur intelligente Zeiger derselben Klasse verwenden, und (2) den Besitz durch Vorlagen verfolgen
Elliot Gorokhovsky
2
@ElliotGorokhovsky Nein, da die Vorlage Kernsprachfunktionen wie Verweise nicht deaktivieren kann. Ein intelligenter Zeiger kann es schwieriger machen, einen Verweis zu erhalten, aber an diesem Punkt bekämpfen Sie die Sprache - die meisten Standardfunktionen der Bibliothek benötigen Verweise. Es ist auch nicht möglich, die Lebensdauer einer Referenz anhand von Vorlagen zu überprüfen, da die Sprache kein einheitliches Konzept für die Lebensdauer bietet.
amon
Ich verstehe, danke
Elliot Gorokhovsky