Warum wird eine Zelle zum Erstellen unbeweglicher Objekte verwendet?

9

Also stieß ich auf dieses Code-Snippet , das zeigt, wie man "unbewegliche" Typen in Rust erstellt - Bewegungen werden verhindert, weil die Compiler behandelt das Objekt für seine gesamte Lebensdauer als ausgeliehen.

%Vor%

Ich verstehe noch nicht, wie das funktioniert. Meine Vermutung ist, dass die Lebensdauer des borrow-pointer-Arguments gezwungen ist, die Lebensdauer des Lock-Feldes zu erreichen. Das Seltsame ist, dass dieser Code auf die gleiche Weise funktioniert, wenn:

  • Ich ändere ContravariantLifetime<'a> in CovariantLifetime<'a> oder in InvariantLifetime<'a> .
  • Ich entferne den Körper der Methode lock .

Aber wenn ich die Cell entferne und einfach lock: marker::ContravariantLifetime<'a> direkt benutze, so:

%Vor%

Dann darf sich das Objekt "Unbeweglich" bewegen. Warum sollte das sein?

    
Aidan Cully 06.02.2015, 22:50
quelle

2 Antworten

3

Die wahre Antwort besteht aus einer mäßig komplexen Betrachtung der Lebensdauer Varianz , mit ein paar irreführenden Ergebnissen Aspekte des Codes, die aussortiert werden müssen.

Für den folgenden Code ist 'a eine beliebige Lebensdauer, 'small ist eine beliebige Lebensdauer, die kleiner ist als 'a (dies kann durch die Einschränkung 'a: 'small ausgedrückt werden), und 'static wird als verwendet das häufigste Beispiel einer Lebensdauer, die größer ist als 'a .

Hier sind die Fakten und Schritte, die bei der Betrachtung zu beachten sind:

  • Normalerweise sind Lebenszeiten kontravariant ; &'a T ist kontravariant in Bezug auf 'a (wie auch T<'a> in Abwesenheit von Varianzmarkern), was bedeutet, dass es, wenn Sie &'a T haben, OK ist, eine längere Lebensdauer als 'a zu ersetzen, z. Sie können an einem solchen Ort ein &'static T speichern und behandeln, als wäre es ein &'a T (Sie dürfen die Lebensdauer verkürzen).

  • An einigen Orten können Lebenszeiten invariant sein ; Das häufigste Beispiel ist &'a mut T , das in Bezug auf 'a invariant ist, was bedeutet, dass wenn Sie &'a mut T haben, Sie nicht &'small mut T darin speichern können (das Borgen reicht nicht lange genug), sondern Sie kann auch keine &'static mut T darin speichern, da dies zu Problemen beim Speichern der Referenz führen würde, da es vergessen würde, dass tatsächlich länger lebte und Sie daher mehrere simultane veränderbare Referenzen erhalten könnten erstellt werden.

  • Ein Cell enthält ein UnsafeCell ; Was nicht so offensichtlich ist, ist, dass UnsafeCell magisch ist und mit dem Compiler für spezielle Behandlung als das Sprachelement namens "unsicher" verknüpft wird. Wichtig ist, dass UnsafeCell<T> in Bezug auf T invariant ist, aus ähnlichen Gründen wie Invarianz von &'a mut T in Bezug auf 'a .

  • Somit verhält sich Cell<any lifetime variancy marker> tatsächlich genauso wie Cell<InvariantLifetime<'a>> .

  • Außerdem müssen Sie Cell nicht mehr verwenden. Sie können einfach InvariantLifetime<'a> verwenden.

  • Zurück zum Beispiel mit dem entfernten Cell -Wrapping und einem ContravariantLifetime (entspricht eigentlich nur der Definition von struct Unmovable<'a>; , da die Kontravarianz der Standardwert ist, wie auch die Copy -Implementierung): Warum lässt es zu? den Wert verschieben? Ich muss gestehen, dass ich diesen speziellen Fall noch nicht kenne und ich würde mir selbst ein wenig helfen, zu verstehen, warum das erlaubt ist. Es scheint von hinten nach vorne, dass die Kovarianz es erlauben würde, dass die Sperre kurzlebig ist, aber dass Kontravarianz und Invarianz nicht funktionieren würden, aber in der Praxis scheint es, dass nur die Invarianz die gewünschte Funktion erfüllt.

Wie auch immer, hier ist das Endergebnis. Cell<ContravariantLifetime<'a>> wird in InvariantLifetime<'a> geändert und das ist die einzige funktionale Änderung, die die lock -Methode wie gewünscht funktioniert und ein Borgen mit einer invarianten Lebensdauer annimmt. (Eine andere Lösung wäre, lock take &'a mut self zu haben, denn eine veränderbare Referenz ist, wie bereits diskutiert, invariant; dies ist jedoch unterlegen, da sie eine unnötige Wandlungsfähigkeit erfordert.)

Noch etwas zu erwähnen: Die Inhalte der Methoden lock und new_in sind völlig überflüssig. Der Rumpf einer Funktion ändert niemals das statische Verhalten des Compilers; nur die Unterschrift zählt. Die Tatsache, dass der Lebensdauerparameter 'a als invariant markiert ist, ist der Schlüsselpunkt. Also ist der ganze "construct% Unmovable -Objekt und Aufruf lock darauf" Teil von new_in völlig überflüssig. Ebenso war das Einstellen des Inhalts der Zelle in lock Zeitverschwendung. (Beachten Sie, dass es wieder die Invarianz von 'a in Unmovable<'a> ist, die new_in funktioniert, nicht die Tatsache, dass es sich um eine veränderbare Referenz handelt.)

%Vor%     
Chris Morgan 08.02.2015, 11:22
quelle
1

Ein interessantes Problem! Hier ist mein Verständnis davon ...

Hier ist ein weiteres Beispiel, das Cell nicht verwendet:

%Vor%

( Laufstall )

Der wichtige Trick hier ist, dass die Struktur sich selbst ausleihen muss. Sobald wir das getan haben, kann es nicht mehr bewegt werden, da jede Bewegung den Kredit ungültig machen würde. Dies ist nicht konzeptionell anders als jede andere Art von Darlehen:

%Vor%

Das einzige ist, dass Sie eine Möglichkeit brauchen, die Struktur ihre eigene Referenz enthalten zu lassen, was zur Konstruktionszeit nicht möglich ist. In meinem Beispiel wird Option verwendet, wofür &mut self benötigt wird, und im verknüpften Beispiel wird Cell verwendet, was eine interne Änderbarkeit und nur &self ermöglicht.

Beide Beispiele verwenden einen Lebenszeitmarker, da das System die Lebenszeit verfolgen kann, ohne sich um eine bestimmte Instanz kümmern zu müssen.

Schauen wir uns Ihren Konstruktor an:

%Vor%

Hier wird die in lock angegebene Lebensdauer vom Aufrufer gewählt und endet als normale Lebensdauer der Unmovable struct. Es gibt kein Eigen leihen.

Sehen wir uns als nächstes Ihre Lock-Methode an:

%Vor%

Hier weiß der Compiler, dass sich die Lebensdauer nicht ändern wird. Wenn wir es jedoch veränderlich machen:

%Vor%

Bam! Es ist wieder gesperrt. Dies liegt daran, dass der Compiler weiß, dass sich die internen Felder ändern können. Wir können dies tatsächlich auf unsere Option -Variante anwenden und den Körper von lock_it entfernen!

    
Shepmaster 07.02.2015 01:52
quelle

Tags und Links