Merkmal 'x' ist nicht für den Typ 'x' implementiert

8

Beim Kompilieren des folgenden Codes:

%Vor%

Ich bekomme den Fehler:

%Vor%

Was ist die Fehlermeldung, die versucht zu sagen? Auch, wie man es repariert?

Es gibt eine verwandte Frage , aber die Lösung bestand darin, das Merkmal A (das entspricht Drawable ) zu ändern. in meinem Fall), aber das ist hier nicht möglich, da Drawable aus einer externen Bibliothek stammt.

    
emlai 18.08.2015, 19:28
quelle

3 Antworten

6

Update: Sicherheitsregeln für feste Objekte auf die Version 1.0 von ihnen. Der by-Wert self macht das Methodenobjekt nicht mehr sicher.

Dieser Fehler tritt aufgrund der Objektsicherheit auf.

Um ein Merkmalsobjekt aus einem Merkmal heraus erstellen zu können, muss das Merkmal objektsicher sein. Ein Merkmal ist objektsicher, wenn beide Aussagen gelten:

  1. es hat nicht die Sized -Anforderung, wie in trait Whatever: Sized {} ;
  2. alle Methoden sind objektsicher.

Eine Methode ist objektsicher, wenn beide Aussagen zutreffen:

  1. es hat where Self: Sized anforderung, wie in fn method() where Self: Sized ;
  2. Keine der folgenden Aussagen gilt:

    1. Diese Methode erwähnt Self in ihrer Signatur in jeder Form, auch unter einer Referenz, außer den zugehörigen Typen;
    2. Diese Methode ist statisch;
    3. Diese Methode ist generisch.

Diese Beschränkungen sind in der Tat ziemlich natürlich, wenn Sie mehr von ihnen denken.

Denken Sie daran, dass bei der Erstellung von Werten in Eigenschaftenobjekten 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 Smart Pointer, 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 Eigenschaftenobjekte nur über einen Zeiger verwendet werden können, kann by-value self methods nicht aufgerufen werden - Sie benötigen den tatsächlichen Wert, um solche Methoden aufzurufen. Dies war eine Verletzung der Objektsicherheit an einem Punkt, was bedeutete, dass Merkmale mit solchen Methoden nicht zu Merkmalsobjekten gemacht werden konnten, aber schon vor 1.0 wurden die Regeln so angepasst, dass Wertobjekte self für Merkmalsobjekte erlaubt wurden. 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, da sie derzeit zu einigen Macken in der Sprache führt, zum Beispiel die Unfähigkeit, Box<FnOnce()> closures zu nennen.

Self kann nicht in Methoden verwendet werden, die auf Merkmalsobjekten 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 für Merkmalsobjekte aufgerufen werden können, liegt auf der Hand - statische Methoden gehören definitionsgemäß "zum Merkmal selbst", nicht zum Wert, also müssen Sie den konkreten Typ kennen, mit dem das Merkmal implementiert wird Ruf Sie an. 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 Merkmalsmethoden 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 müsste die virtuelle Tabelle, um generische Methoden für ein Merkmalsobjekt aufzurufen, Zeiger auf virtuell jede mögliche Instanzierung der generischen Methode für alle möglichen Typen enthalten, die ist natürlich unmöglich, weil es unendlich viele Instanziierungen erfordern würde. Und so ist es nicht erlaubt, generische Methoden für Merkmalsobjekte aufzurufen.

Wenn Drawable ein externes Merkmal ist, dann stecken Sie fest - es ist unmöglich, das zu tun, was Sie wollen, dh 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 für jeden ziehbaren Typ eine Variante enthält. Dann können Sie Drawable für die Enumeration selbst implementieren, was ziemlich einfach wäre.

    
Vladimir Matveev 18.08.2015, 20:15
quelle
3

Ich beziehe mich auf Wladimirs ausgezeichnete Antwort, die die Sicherheit von Object erklärt, jedoch habe ich Angst, dass mitten in der Diskussion das konkrete Problem vergessen wurde.

Wie Vladimir erwähnt, besteht das Problem darin, dass eine generische Methode über Typen (generisch über Lebensdauern ist in Ordnung) die Eigenschaft, zu der sie gehört, für den Laufzeitpolymorphismus unbrauchbar macht; Dies wird in Rust als Objektsicherheit bezeichnet.

Die einfachste Lösung ist daher, den generischen Parameter der Methode zu entfernen!

%Vor%

Der Hauptunterschied zwischen:

%Vor%

und

%Vor%

ist, dass letzteres% RenderTarget ebenfalls zu Object Safe macht, da es jetzt in einer Laufzeitpolymorphismus-Situation verwendet wird (also keine statische Methode, keine generische Methode, keine Self , ...).

Ein anderer (technischerer) Unterschied besteht darin, dass ersterer zur Kompilierungszeit "monophonisiert" wird (das heißt RT wird durch den realen Typ ersetzt und alle relevanten Optimierungen angewendet), während letzterer nicht (und so, kein solcher) ist Optimierungen auftreten).

    
Matthieu M. 19.08.2015 06:44
quelle
2

Wenn Sie mit dem, was Sie bekommen haben, nicht weiterkommen, gibt es zwei Möglichkeiten, die Sie ausprobieren könnten.

In diesem Fall können Sie nicht, aber wenn Sie eine nicht standardisierte RenderTarget

erhalten haben %Vor%

Sie könnten

implementieren %Vor%

Umleiten der übergebenen Typen an eine objektsichere dynamisch versendete Alternative. Es sieht so aus, als könnte eine solche Änderung stromaufwärts gemacht werden, da Sie nicht wirklich verwenden können, weil RT eine Größe hat.

Der andere erlaubt es nicht, beliebige Drawable s in Ihre Vec zu setzen, sollte aber funktionieren, ohne dass Upstream-Typen in der Größe erlaubt werden. Um die möglichen Werte des Vektors zu umschließen, verwenden Sie eine Aufzählung:

%Vor%

Vielleicht möchten Sie From implementations und so hinzufügen; Sie könnten es einfacher finden, wenn Sie wrapped_enum! verwenden, was diese automatisch für Sie implementiert.

    
Veedrac 18.08.2015 21:43
quelle