Ich habe Probleme, die Regeln für Merkmale in algebraischen Datentypen zu verstehen. Hier ist ein vereinfachtes Beispiel:
%Vor%Das obige kann nicht kompiliert werden, die folgende Fehlermeldung wird ausgegeben:
%Vor% Warum ist pond.push(duck)
gültig, aber lake.push(mallard)
nicht? In beiden Fällen wurde ein Duck
geliefert, wo ein Quack
erwartet wurde. In der ersten ist der Compiler glücklich, aber in der zweiten ist es nicht.
Liegt der Grund für diesen Unterschied in CoerceUnsized
?
Das ist ein korrektes Verhalten, auch wenn es etwas unglücklich ist.
Im ersten Fall haben wir folgendes:
%Vor% Beachten Sie, dass push()
, wenn Vec<Box<Quack>>
aufgerufen wird, Box<Quack>
akzeptiert und Sie Box<Duck>
übergeben. Das ist in Ordnung - rustc kann nachvollziehen, dass Sie einen Boxed-Wert in ein Trait-Objekt wie hier konvertieren möchten:
Im zweiten Fall haben wir das:
%Vor% Hier akzeptiert push()
Rc<RefCell<Box<Quack>>>
, während Sie Rc<RefCell<Box<Duck>>>
:
Und jetzt gibt es Ärger. Box<T>
ist ein DST-kompatibler Typ und kann daher als Container für ein Merkmalsobjekt verwendet werden. Das gleiche gilt bald auch für Rc
und andere intelligente Zeiger, wenn Dieser RFC ist implementiert. In diesem Fall gibt es jedoch keinen Zwang von einem konkreten Typ zu einem Merkmalsobjekt, da sich Box<Duck>
innerhalb von zusätzlichen Typenschichten befindet ( Rc<RefCell<..>>
).
Denken Sie daran, dass das Merkmalobjekt ein Fettzeiger ist, also unterscheidet sich Box<Duck>
von Box<Quack>
in der Größe. Folglich sind sie im Prinzip nicht direkt kompatibel: Sie können nicht einfach Bytes von Box<Duck>
nehmen und sie dorthin schreiben, wo Box<Quack>
erwartet wird. Rust führt eine spezielle Konvertierung durch, dh es ruft einen Zeiger auf die virtuelle Tabelle für Duck
ab, erstellt einen Fettzeiger und schreibt ihn in Box<Quack>
-typisierte Variable.
Wenn Sie Rc<RefCell<Box<Duck>>>
haben, müsste rustc jedoch wissen, wie Sie sowohl RefCell
als auch Rc
konstruieren und destrukturieren, um dieselbe Fettzeigerkonvertierung auf seine Interna anzuwenden. Da dies Bibliothekstypen sind, kann es natürlich nicht wissen, wie es geht. Dies gilt auch für jeden anderen Wrappertyp, z. Arc
oder Mutex
oder sogar Vec
. Sie erwarten nicht, dass Vec<Box<Duck>>
als Vec<Box<Quack>>
verwendet werden könnte, oder?
Es gibt auch eine Tatsache, dass in dem Beispiel mit Rc
die aus Box<Duck>
und Box<Quack>
erstellten Rcs nicht verbunden worden wären - sie hätten unterschiedliche Referenzzähler gehabt.
Das bedeutet, dass eine Umwandlung von einem konkreten Typ in ein Merkmalsobjekt nur stattfinden kann, wenn Sie direkten Zugriff auf einen Smart-Zeiger haben, der DST unterstützt, nicht wenn er in einer anderen Struktur verborgen ist.
Das heißt, ich sehe, wie es möglich ist dies für einige ausgewählte Typen zu ermöglichen. Zum Beispiel könnten wir eine Art von Construct
/ Unwrap
Merkmalen einführen, die dem Compiler bekannt sind und die es verwenden könnten, innerhalb eines Stapels von Wrappern zu "erreichen" und darin die Umwandlung von Objekteigenschaften durchzuführen. Allerdings hat noch niemand dieses Ding entworfen und einen RFC darüber zur Verfügung gestellt - wahrscheinlich, weil es kein weit verbreitetes Feature ist.
Vladimirs Antwort erklärte, was
Compiler tut. Basierend auf diesen Informationen entwickelte ich eine Lösung: Erstellen eines Wrappers
struct um Box<Quack>
.
Der Wrapper heißt QuackWrap
. Es hat eine feste Größe, und es kann wie jeder andere verwendet werden
andere Struktur (denke ich). Die Box
in% QuackWrap
erlaubt mir ein QuackWrap
zu erstellen
um irgendein Merkmal, das Quack
implementiert. So kann ich ein Vec<Rc<RefCell<QuackWrap>>>
haben
wobei die inneren Werte eine Mischung aus Duck
s, Goose
s usw. sind.
Als zusätzlichen Komfort möchte ich wahrscheinlich Deref
und DefrefMut
implementieren
%Code%. Aber das ist für das obige Beispiel nicht notwendig.
Tags und Links rust