Was ist 'core :: arten :: Sized' für den Typ 'Self' in Rost nicht implementiert?

8

Das hat früher funktioniert:

%Vor%

Jetzt ist es falsch:

  

: 15: 21: 15:25 Fehler: Das Merkmal core::kinds::Sized ist für den Typ Self nicht implementiert.   : 15 zurück Foo :: neu (selbst);

Ich kann irgendwie raten, was falsch ist; es sagt, dass mein Impl von Foo & lt; a, T & gt; ist für T, nicht Größe? T, aber ich versuche nicht, ein Sized zu speichern? Element darin; Ich speichere eine Referenz auf ein Sized-Element darin. Das sollte ein Zeiger sein, feste Größe.

Ich sehe nicht was falsch ist mit dem, was ich mache, oder warum es falsch ist?

Zum Beispiel sollte ich (denke ich ...) ein & amp; Array in meinem Foo speichern können, kein Problem. Ich kann keinen Grund dafür sehen, dass dies meine Foo-Instanz dazu zwingen würde, unbemannt zu sein.

Laufstall-Link: Ссылка

    
Doug 07.01.2015, 09:20
quelle

1 Antwort

13

Es gibt zwei Dinge, die hier vor sich gehen: Eigenschaften von Objektobjekten (der Fehler) und Objektsicherheit (Korrektur).

Der Fehler

Wie von der Fehlermeldung vorgeschlagen, ist der schwierige Teil des Codes Foo::new(self) , und zwar deshalb, weil pub fn new<T>(parent: &Array<T>) -> ... , das heißt self , zu einem &Array<T> - Objektobjekt gezwungen wird. Ich werde den Code vereinfachen:

%Vor%

Das gibt das gleiche:

%Vor%

Self ist der Ersatzname für den Typ, der das Merkmal implementiert. Im Gegensatz zu den meisten generischen Parametern ist Self standardmäßig nicht standardisiert ( ?Sized ), da RFC 546 und # 20341 für die Zwecke zu erlauben z impl Array<T> for Array<T> arbeitet standardmäßig öfter (wir kommen später dazu).

Die Variable self hat den Typ &Self . Wenn Self ein typisierter Typ ist, ist dies eine normale Referenz: ein einzelner Zeiger. Wenn Self ein nicht standardisierter Typ ist (wie [T] oder ein Merkmal), dann ist &Self ( &[T] oder &Trait ) ein Slice / Merkmal-Objekt: ein dicker Zeiger.

Der Fehler tritt auf, weil die einzigen Referenzen &T , die in ein Merkmalsobjekt umgewandelt werden können, sind, wenn T eine Größe hat: Rust unterstützt nicht die Erstellung von Fettzeigern, nur der dünne Zeiger → Fettzeiger ist gültig. Da der Compiler nicht weiß, dass Self immer Sized ist (denken Sie daran, es ist speziell und ?Sized standardmäßig), muss er das Schlimmste annehmen: dass der Zwang nicht ist legal, und so ist es nicht erlaubt.

Es reparieren

Es erscheint logisch, dass wir nach dem Fix suchen, um Self: Sized zu sichern, wenn wir einen Zwang ausüben wollen. Die naheliegendste Möglichkeit wäre, Self immer Sized zu erstellen, das heißt, die standardmäßige ?Sized -Bindung wie folgt zu überschreiben:

%Vor%

Sieht gut aus!

Außer dem kleinen Punkt, dass es nicht funktioniert; aber zumindest aus einem anderen Grund machen wir Fortschritte! Merkmalsobjekte können nur aus Merkmalen gemacht werden, die "objektsicher" sind (d. H. Sicher zu einem Merkmalsobjekt gemacht werden können), und Sized Self ist eines der Dinge, die die Objektsicherheit beeinträchtigen:

%Vor%

(Ich habe den doppelten Druck der Note als # 20692 eingereicht.)

Zurück zum Zeichenbrett. Es gibt ein paar andere "einfache" Möglichkeiten für eine Lösung:

  • Definieren Sie ein Erweiterungsmerkmal trait ArrayExt: Sized + Array { fn as_foo(&self) { ... } } und implementieren Sie es für alle Sized + Array -Typen
  • benutze einfach eine freie Funktion fn array_as_foo<A: Array>(x: &A) { ... }

Diese arbeiten jedoch nicht notwendigerweise für jeden Anwendungsfall, z.B. Bestimmte Typen können das Verhalten nicht anpassen, indem sie die Standardmethode überlasten. Zum Glück gibt es eine Lösung!

Der Turon-Trick

(Benannt nach Aaron Turon, der es entdeckt hat.)

Mit verallgemeinerten where -Klauseln können wir sehr genau festlegen, wann Self Sized implementieren soll, indem wir es auf genau die Methode (n) beschränken, wo es benötigt wird, ohne den Rest des Merkmals zu infizieren:

%Vor%

Das kompiliert einfach gut! Durch die Verwendung der where -Klausel versteht der Compiler, dass (a) der Zwang legal ist, weil Self ist Sized , also self ist ein dünner Zeiger, und (b) dass die Methode illegal aufgerufen wird ein Objektobjekt sowieso, und so wird die Objektsicherheit nicht beeinträchtigt. Um zu sehen, dass es nicht erlaubt ist, ändere den Körper von as_foo in

%Vor%

gibt

%Vor%

wie erwartet.

Alles einpacken

Wenn Sie diese Änderung auf den ursprünglichen nicht vereinfachten Code anwenden, fügen Sie einfach die where -Klausel zur as_foo -Methode hinzu:

%Vor%

das kompiliert ohne Fehler. (NB. Ich musste die unnötige <T> in pub fn new<T> entfernen, da dies zu Fehlfunktionen führte.)

(Ich habe einige Blogeinträge in Bearbeitung, die in die Objektobjekte, die Objektsicherheit und den Turon-Trick einfließen, sie erscheinen auf / r / rost in naher Zukunft: erste . )

    
huon 07.01.2015, 12:56
quelle

Tags und Links