Update: festen Objekt Sicherheitsregeln auf die Version 1.0 von ihnen. Nämlich, By-Wert self
macht Methode Objekt-unsicher nicht mehr.
Dieser Fehler tritt wegen object safety auf.
Um ein Merkmalsobjekt außerhalb eines Merkmals erstellen zu können, muss das Merkmal objektsicher sein. Ein Merkmal ist objekt sicher, wenn diese beiden Aussagen:
- es hat nicht
Sized
Anforderung, wie in trait Whatever: Sized {}
;
- alle ihre Methoden sind objekt sicher.
Verfahren ist objekt sicher, wenn beide Aussagen zutreffen:
- es
where Self: Sized
Bedarf hat, wie in fn method() where Self: Sized
;
keine der folgenden Aussagen gilt:
- Diese Methode erwähnt
Self
in ihrer Signatur in jeder Form, selbst unter einer Referenz, mit Ausnahme zugehörigen Typen;
- Diese Methode ist statisch;
- diese Methode ist generisch.
Diese Einschränkungen sind in der Tat recht natürlich, wenn Sie mehr von ihnen denken.
Denken Sie daran, dass bei der Erstellung von Werten in Merkmalsobjekten tatsächliche Informationen ihres Typs einschließlich ihrer Größe gelöscht werden. Daher können Merkmalsobjekte nur über eine Referenz verwendet werden. Verweise (oder andere intelligente Zeiger, wie Box
oder Rc
) werden, wenn sie auf Merkmalsobjekte angewendet werden, "Fat Pointer" - zusammen mit dem Zeiger auf den Wert enthalten sie auch einen Zeiger auf die virtuelle Tabelle für diesen Wert.
Da Merkmalsobjekte nur über einen Zeiger verwendet werden können, können die Methoden nicht aufgerufen werden - Sie benötigen den tatsächlichen Wert, um solche Methoden aufzurufen. Dies war eine Verletzung von Objektsicherheit an einem Punkt, was dazu führte, dass Züge mit solchen Methoden nicht Charakterzug Objekte gemacht werden können, jedoch noch vor 1,0 die Regeln gezwickt worden durch Wert self
Methoden auf Charakterzug Objekte zu ermöglichen. Diese Methoden können jedoch aus dem oben beschriebenen Grund immer noch nicht aufgerufen werden.Es gibt Gründe zu erwarten, dass diese Einschränkung in Zukunft aufgehoben wird, weil sie derzeit zu einigen Eigenheiten in der Sprache führt, zum Beispiel die Unfähigkeit, Box<FnOnce()>
Schließungen zu nennen.
Self
kann nicht in Methoden verwendet werden, die auf Merkmalsobjekte genau aufgerufen werden sollten, weil Merkmalsobjekte ihren tatsächlichen Typ gelöscht haben, aber um solche Methoden aufzurufen, müsste der Compiler diesen gelöschten Typ kennen.
Warum statische Methoden nicht auf Merkmalsobjekte aufgerufen werden können, ist offensichtlich - statische Methoden gehören definitionsgemäß zu dem Merkmal selbst, nicht zum Wert, also müssen Sie den konkreten Typ kennen, der das Merkmal implementiert um sie anzurufen. Konkreter gesagt, werden reguläre Methoden über eine virtuelle Tabelle gesendet, die in einem Merkmalsobjekt gespeichert ist. Statische Methoden haben jedoch keinen Empfänger, so dass sie nicht weitergeleitet werden müssen. Aus diesem Grund können sie nicht in einer virtuellen Tabelle gespeichert werden. So sind sie unkündbar, ohne den konkreten Typ zu kennen.
Generische Trait-Methoden können nicht aus einem anderen Grund aufgerufen werden, technischer als logischer, denke ich. In Rust werden generische Funktionen und Methoden durch Monomorphisierung implementiert - dh für jede Instanzierung einer generischen Funktion mit einer bestimmten Menge von Typparametern generiert der Compiler eine separate Funktion. Für den Sprachbenutzer sieht es so aus, als würden sie eine generische Funktion aufrufen; aber auf der untersten Ebene für jede Menge von Typparametern gibt es eine separate Kopie der Funktion, die darauf spezialisiert ist, für die instanziierten Typen zu arbeiten.
Bei diesem Ansatz, um generische Methoden auf einem Merkmalsobjekt aufrufen zu können, müsste seine virtuelle Tabelle Zeiger auf virtuell jede mögliche Instanz der generischen Methode für alle möglichen Typen enthalten, was natürlich unmöglich, weil es eine unendliche Anzahl von Instanziierungen erfordern würde. Und so ist das Aufrufen generischer Methoden für Merkmalsobjekte nicht erlaubt.
Wenn Drawable
ein externes Merkmal ist, dann stecken Sie fest - es ist unmöglich zu tun, was Sie wollen, nämlich draw()
für jedes Element in einer heterogenen Sammlung aufzurufen. Wenn Ihre Zeichensatzgruppe statisch bekannt ist, können Sie für jeden Zeichensatz eine eigene Sammlung erstellen oder alternativ Ihre eigene enum
erstellen, die eine Variante für jeden ziehbaren Typ enthalten würde. Dann können Sie Drawable
für das Enum selbst implementieren, was ziemlich einfach wäre.
"Sie benötigen ihre virtuelle Tabelle, um Zeiger auf praktisch jede mögliche Instanziierung der generischen Methode für alle möglichen Typen zu enthalten, was [...] eine unendliche Anzahl von Instanziierungen erfordert." Nein, würde es nicht - es gibt nur eine endliche Anzahl von Typen und nur eine kleine Teilmenge wird tatsächlich im Code verwendet. Es scheint möglich zu sein, diese statisch zu erkennen, wenn es die Kosten nicht wert ist. – Veedrac
"gibt es nur eine endliche Anzahl von Typen" wirklich? Es gibt unendlich viele Arten, und hier ist ein Beweis. Wenn Sie von diesen Aussagen ausgehen: 'i32 in Typen',' für alle T.Option in Typen' (die offensichtlich wahr sind), können Sie leicht alle 'i32',' Option ',' Option
Ich würde auch hinzufügen, dass es aussieht, als ob es Probleme mit der Interoperabilität gibt. Angenommen, eine Eigenschaft mit einer generischen Methode ist in einer Kiste definiert und ihre Merkmalsobjekte werden in derselben Kiste verwendet. Es müsste eine Art virtueller Tisch für diese Kiste erzeugt werden. Jetzt wird diese Kiste von einer anderen Kiste benutzt, die auch Merkmalsobjekte für das gleiche Merkmal benutzt, aber mit unterschiedlichen Typparametern für ihre Methode. Jetzt haben wir zwei inkompatible virtuelle Tabellen, und so sind Merkmalsobjekte, die in der ersten und der zweiten Kiste erzeugt werden, nicht kompatibel. Es wird sogar noch lustiger, wenn wir anfangen, gemeinsame Bibliotheken zu benutzen. –