Rust: 🔬 Tracking-Problem für generische assoziierte Typen (GAT)

Erstellt am 2. Sept. 2017  ·  67Kommentare  ·  Quelle: rust-lang/rust

Hilfreichster Kommentar

https://github.com/rust-lang/rust/issues/44265#issuecomment -568247656 ist ein (etwas knappes) Update.

67510 ist das letzte große ICE/fehlende Feature, das implementiert werden muss.

Alle 67 Kommentare

Hier ist eine Art Umsetzungsplan, den ich bemühen werde, auf dem Laufenden zu halten.

  • [ ] Schritt eins: Unterstützung in AST und Pretty-Printing hinzufügen

    • Wahrscheinlich besteht der erste Schritt darin, mit dem Parsen der neuen Formulare zu beginnen und dem AST Unterstützung für sie hinzuzufügen.

    • Siehe diesen Kommentar für detailliertere Gedanken hier .

    • Wir sollten in der Lage sein, einige reine Analysetests zu schreiben und auch den hübschen Drucker hir zu testen

    • Wenn wir zur HIR-Senkung kommen, können wir feststellen, ob GAT vorhanden sind

    • Wir können dann auch das Feature-Gate machen

  • [ ] Da kommt noch mehr

Lassen Sie mich damit beginnen, etwas ausführlicher über den AST zu schreiben. Lassen Sie uns zuerst besprechen, wie es heute funktioniert:

Ein type Foo: Bar [= Baz]; -Element in einer Merkmalsdefinition wird durch diese AST-Variante definiert . Dazu gehören die Grenzen ( Bar ) und der (optionale) Standardwert Baz . Der Name wird in der Struktur TraitItem definiert.

Ein type Foo = Bar; -Element in einem Trait-Impl wird durch diese AST-Variante definiert – die nur den Typ Bar enthält, da Foo usw. in ImplItem definiert ist.

Methoden sind ein interessanter Fall, weil sie bereits generisch gemacht werden können. Diese generischen Parameter werden im Feld Generics der Struktur MethodSig deklariert. Dies ist eine Instanz der Struktur Generics .

Meiner Meinung nach wäre es am besten, Generics aus Methoden in TraitItem (und ImplItem ) zu "heben", damit es für alle Formen von gleichermaßen gilt Trait- und Impl-Items. Im Moment werden wir keine generischen Konstanten unterstützen, denke ich, aber ehrlich gesagt fallen sie wahrscheinlich sowieso aus unserer Arbeit heraus, also wären sie eine kleine Erweiterung. Ich denke, die Arbeit wird besser laufen, wenn wir sie jetzt planen.

Vielleicht wäre es eine anständige erste PR, einfach diese Änderung vorzunehmen und alle anderen vorhandenen Funktionen gleich zu lassen. Das heißt, wir würden mehr Generics in TraitItem (und ImplItem ) und aus MethodSig herausgeben. Für Nicht-Methoden würden wir ein leeres Generics angeben. Wir würden den vorhandenen Code durcharbeiten und die Generika nach Bedarf mit einfließen lassen, damit er funktioniert.

@nikomatsakis Cool! Ich danke dir sehr! Ich habe gestern Abend angefangen, damit zu experimentieren, und ich bin stolz darauf, sagen zu können, dass ich dieselben Stellen gefunden habe, auf die Sie in Ihrem Kommentar zum AST hingewiesen haben. :smile: (Angesichts der Tatsache, dass dies mein erstes Mal bei Rustc war, zähle ich das als eine Leistung!)

Ich habe nicht daran gedacht, Generika in TraitItem zu heben. Mein Ansatz bestand darin, Generics in TraitItemKind::Type einzufügen, da dort bereits die Typdeklaration gespeichert ist. Ihr Ansatz macht auch Sinn, also werde ich daran arbeiten, das umzusetzen. Da ich noch völlig neu in dieser Codebasis bin, möchte ich wissen, was die Fallstricke meines Ansatzes wären, wenn er über dem von Ihnen vorgeschlagenen verwendet würde. Können Sie mir einen Einblick in Ihren Denkprozess geben? :smiley:

Hier wäre die Änderung, die ich vorgenommen hätte:

pub enum TraitItemKind {
    // Generics aren't supported here yet
    Const(P<Ty>, Option<P<Expr>>),
    // `Generics` is already a field in `MethodSig`
    Method(MethodSig, Option<P<Block>>),
    // Added `Generics` here:
    Type(Generics, TyParamBounds, Option<P<Ty>>),
    Macro(Mac),
}

Edit: Antwort von Nikomatsakis auf Gitter

in Bezug auf die Fallstricke, sie in Typ zu setzen
Ich denke das könnte auch funktionieren
der Grund, warum ich das nicht wollte
ist, dass wir (zumindest theoretisch) für Methoden und Typen wirklich dasselbe tun wollen
und – wie gesagt – im Prinzip sehe ich keinen Grund, warum wir nicht dasselbe für Konstanten tun könnten
Ich denke, wenn Sie einfach die Generics auf die Typvariante verschieben
das würde wahrscheinlich funktionieren, aber wenn Sie sich umsehen, müssen wir im Moment oft "eine Sache für Typen / Konstanten, eine Sache für Methoden" tun, gerade weil sie unterschiedlich sind
Ich vermute also, dass der Code nur einheitlicher wird
Ich bin mir nicht wirklich sicher, wie es gehen wird, um ehrlich zu sein =) - es könnte ein Schmerz sein
aber oft ist es nicht so schlimm, Dinge allgemeiner zu haben, als sie sein müssen, weil Sie span_bug einfügen können! Anrufe in den unmöglichen Fällen für jetzt (und später kommen wir herum und flicken sie)

Alles klar! Der nächste Schritt besteht darin, den Parser zu erweitern. Hier sind ein paar Tipps. Beginnen wir mit Eigenschaftsgegenständen.

Diese Routine parst Merkmalselemente . Wir wollen den Fall erweitern, der assoziierte Typen behandelt , um auch Dinge wie type Foo<....> = ...; (auch vielleicht Where-Klauseln) zu parsen. (Die <...> sind die „Generika“, die wir gerade zum AST hinzugefügt haben.)

Derzeit verwendet es parse_ty_param , das im Grunde so etwas wie T: Foo usw. analysiert. Wir müssen damit aufhören, da die Grammatik für zugehörige Typdeklarationen nicht mehr mit der für Typparameter übereinstimmt. Wir werden also wahrscheinlich etwas wie parse_trait_item_assoc_ty hinzufügen wollen. Dies kann als eine Art Klon von parse_ty_param() beginnen, aber dann wollen wir es modifizieren, um genau hier parse_generics() aufzurufen . Diese Routine analysiert eine Generika-Deklaration ( <...> ), falls eine vorhanden ist, andernfalls gibt sie nur eine leere Generika-Deklaration zurück. Dann wollen wir hier einen Aufruf zum Parsen von where -Klauseln hinzufügen – Sie können dies nach dem Aufruf modellieren, der beim Parsen von methods auftritt , beachten Sie, dass das Ergebnis in generics gespeichert wird, das wir zuvor geparst haben.

Sobald wir das getan haben, sollten wir in der Lage sein, einige Parsing-Tests hinzuzufügen. Ich würde das tun, indem ich ein Verzeichnis wie src/test/run-pass/rfc1598-generic-associated-types/ erstelle und Dateien hinzufüge, von denen Sie erwarten, dass sie dort erfolgreich analysiert werden. Im Moment werden sie nicht richtig funktionieren , aber das macht nichts. Fügen Sie einfach eine leere Hauptfunktion hinzu. Dann können wir auch Beispiele hinzufügen, die nicht in src/test/ui/rfc1598-generic-associated-types/ geparst werden sollten (siehe COMPILER_TESTS.md für Anweisungen zum Hinzufügen von UI-Tests ).

Etwas anderes – wir müssen diese Arbeit an dieser Stelle mit Gate versehen, um zu vermeiden, dass Leute dieses Zeug in stabilen Builds verwenden. Es gibt einige Anweisungen zum Hinzufügen eines Feature-Gates hier auf Forge (siehe letzter Abschnitt). Wir sollten visit_trait_item und visit_impl_item zum Besucher in feature_gate.rs hinzufügen; Wenn dieses Element keine Methode ist, aber ein nicht leeres Generikum hat, können wir gate_feature_post ( example ) aufrufen.

Ich denke, alles, was wir tun müssen, um die Namensauflösung einzurichten, ist, die richtigen "Rippen" an Ort und Stelle zu bringen (das Zeug zur Namensauflösung organisiert die Namenssätze, die sich im Geltungsbereich befinden, in Rippen; jede Rippe repräsentiert eine Bindungsebene). zB für ein impl:

impl<A,B> Foo<B> for Vec<A> {
   fn bar<T,U>(x: ...) { 
       for y in ... {
       }
   }
}

Wir hätten folgende Rippen:

- <A,B> (from the impl)
   - <T,U> (from the `bar` method's generics)
      - `x` (from the parameter list)
          - `y` (from the let)

Im Allgemeinen ist es keine schlechte Idee, Dinge nach der Funktionsweise von Methoden zu modellieren. Wir könnten hier auch ein bisschen "zukunftssicher" machen, nehme ich an.

Hier ist der Code, der die Typparameter einer Methode in den Geltungsbereich bringt (dies gilt für eine Methode, die in einer Eigenschaft definiert ist):

https://github.com/rust-lang/rust/blob/a35a3abcda67a729edbb7d649dbc663c6feabd4c/src/librustc_resolve/lib.rs#L1890 -L1892

Während wir für ein in einem Merkmal definiertes type fest codiert sind, um einen leeren Typparameter rib ( NoTypeParameters ) hinzuzufügen:

https://github.com/rust-lang/rust/blob/a35a3abcda67a729edbb7d649dbc663c6feabd4c/src/librustc_resolve/lib.rs#L1897 -L1901

Jetzt, da Generika für jedes Trait/Impl-Element vorhanden sind, sollten wir wahrscheinlich die Handhabung für type entfernen und die Methodenhandhabung extrahieren, damit sie auf einer höheren Ebene auftritt. Für die Artikel (zB const ), wo es keine Generika gibt, sollte die neu eingeführte Rippe leer und daher harmlos sein (hoffe ich).

Weitere Sehenswürdigkeiten:

Du hast die Idee.

@petrochenkov - klingt ungefähr richtig?

@Nikomatsakis

klingt etwa richtig?

Alles sieht richtig aus.

Nächster Schritt. Lebenslange Auflösung.

Im Guten wie im Schlechten wird dies derzeit in einem völlig separaten Code-Bit von anderen Namensauflösungen durchgeführt. Dies liegt daran, dass es nach dem Bau des HIR stattfindet. Mit ziemlicher Sicherheit wird sich das ändern, aber es hat sich noch nicht geändert.

Die Grundideen sind jedoch die gleichen wie bei der normalen Namensauflösung, außer dass wir die Dinge nicht "Ribs", sondern "Scopes" nennen. =)

Aufgrund dieses Konzepts der "späten Bindung" von Lebenszeiten gibt es einige leichte Komplikationen. Es ist hier jedoch nicht wirklich relevant - alle Lebensdauern für einen generischen zugeordneten Typ sind "früh gebunden", was ein ziemlich einfacher Fall ist. Eine "späte gebundene" Lebensdauer ist eine Lebensdauer, die für eine Methode oder Funktion deklariert wird, deren Wert nicht bereitgestellt wird, bis die Methode aufgerufen wird. Ich gehe hier nicht ins Detail, weil es nicht so relevant ist -- Hauptsache, wir wollen anders als bei der anderen Namensauflösung nicht genau dem gleichen Modell für Methoden folgen wie für andere Arten von generischen Artikeln Fälle.

Hier ist ein Beispielcode. Dies ist der Code, der ein impl , struct oder ein anderes Nicht-Funktionselement besucht. In diesen Fällen möchten wir, wie in GATs, im Grunde alle Lebensdauerparameter aus einem Generics in den Geltungsbereich bringen und sie auf "früh gebundene" Lebensdauern abbilden:

https://github.com/rust-lang/rust/blob/ddaebe938b8eb1f5e17570ae8091743972e02bdd/src/librustc/middle/resolve_lifetime.rs#L370 -L388

Sie können sehen, dass es zuerst einen Vektor von Lebenszeiten erstellt und für jedes Region::early aufruft:

https://github.com/rust-lang/rust/blob/ddaebe938b8eb1f5e17570ae8091743972e02bdd/src/librustc/middle/resolve_lifetime.rs#L376 -L378

Als nächstes erstellt es ein Scope . Die next_early_index -Variable zählt nur, wie viele früh gebundene Lebensdauern im Bereich sind. Da dieser Code spezifisch für Artikel ist, ist dies immer die Anzahl der früh gebundenen Lebensdauern, die für diesen aktuellen Artikel deklariert sind. (Später werden wir uns einen Fall ansehen, in dem wir zusätzliche Lebensdauern in den Geltungsbereich einbeziehen, was wir für GATs eher wollen.)

https://github.com/rust-lang/rust/blob/ddaebe938b8eb1f5e17570ae8091743972e02bdd/src/librustc/middle/resolve_lifetime.rs#L379 -L384

Schließlich rufen wir with() . Diese Methode bringt den Geltungsbereich in den Geltungsbereich und ruft eine Schließung auf. Alle Lebenszeiten, die wir innerhalb dieser Schließung besuchen, werden die Namen sehen, die wir gerade als im Geltungsbereich definiert haben:

https://github.com/rust-lang/rust/blob/ddaebe938b8eb1f5e17570ae8091743972e02bdd/src/librustc/middle/resolve_lifetime.rs#L385 -L388

OK, schauen wir uns jetzt ein weiteres Beispiel an. Dieser Fall betrifft "immpl traits". Die Details dessen, was es tut, sind nicht so wichtig (das heißt, Sie müssen nicht unter die impl Trait Entzuckerung per se gehen). Es genügt zu sagen, dass es einige neue früh gebundene Lebenszeiten in den Geltungsbereich bringt – das ist genau das, was wir für GATs tun wollen.

https://github.com/rust-lang/rust/blob/ddaebe938b8eb1f5e17570ae8091743972e02bdd/src/librustc/middle/resolve_lifetime.rs#L482 -L501

Lassen Sie mich einige Dinge hervorheben. Zunächst einmal gibt die Methode next_early_index den nächsten nicht zugewiesenen früh gebundenen Index zurück:

https://github.com/rust-lang/rust/blob/ddaebe938b8eb1f5e17570ae8091743972e02bdd/src/librustc/middle/resolve_lifetime.rs#L488

Das bietet einen Ansatzpunkt. Als Nächstes verwenden wir erneut Region::early , um neue früh gebundene Lebensdauerdefinitionen zu erstellen, gegen die aufgelöst wird:

https://github.com/rust-lang/rust/blob/ddaebe938b8eb1f5e17570ae8091743972e02bdd/src/librustc/middle/resolve_lifetime.rs#L490 -L492

Schließlich bringen wir diese in den Geltungsbereich, indem wir erneut with aufrufen:

https://github.com/rust-lang/rust/blob/ddaebe938b8eb1f5e17570ae8091743972e02bdd/src/librustc/middle/resolve_lifetime.rs#L494 -L501

OK, das sind zwei Beispiele. Wir werden etwas Ähnliches wie das zweite machen wollen. Wir möchten die Definitionen dieser beiden Methoden ändern:

https://github.com/rust-lang/rust/blob/ddaebe938b8eb1f5e17570ae8091743972e02bdd/src/librustc/middle/resolve_lifetime.rs#L509

https://github.com/rust-lang/rust/blob/ddaebe938b8eb1f5e17570ae8091743972e02bdd/src/librustc/middle/resolve_lifetime.rs#L520

Beide müssen für zugeordnete Typen die zugeordneten Generika verarbeiten. (In den anderen Fällen sollten sie wahrscheinlich behaupten, dass die Generika leer sind.)

Also habe ich mich heute früh darauf eingelassen. Ich möchte mich vergewissern, dass ich auf dem richtigen Weg bin:

  • Alles, was getan werden musste, war, die Lebenszeiten auf der Karte einzuführen und dann den Trait/Impl-Gegenstand zu gehen? Die Überprüfung scheint zu funktionieren, obwohl es schwer zu sagen ist (siehe später) ... funktioniert möglicherweise nur im einfachen (unbegrenzten) Fall.
  • Ich habe die Typparameter-Verbote für qpath_to_ty und associated_path_def_to_ty in librustc_typeck/astconv.rs #$ aufgehoben, um die type parameters are not allowed on this type -Fehler zu beheben. Ich denke, das muss durch einige Kontrollen ersetzt werden. Ebenfalls...
  • Ich bekomme jetzt Abstürze von Typeck. (speziell Rückschreiben)

typeck-Fehler werden src/test/compile-fail/struct-path-associated-type.rs ausgelöst, weil es Generika für Werte bereitstellt, die keinen zugeordneten Typ haben.

Wenn ich die Dinge richtig lese, muss ich zumindest eine Überprüfung hinzufügen, ob die zugehörigen generischen Zählwerte übereinstimmen (versuchen herauszufinden, wo das zu tun ist ...), und möglicherweise auch andere Überprüfungen durchführen, um Typen für Knoten usw. hinzuzufügen.

Ich werde daran arbeiten, aber Hinweise darauf, ob ich überhaupt in die richtige Richtung gehe, sind willkommen.

Hallo @brandonson! Ich möchte nur ungern jemanden davon abhalten, den Rust-Compiler zu hacken, aber ich denke, dass @sunjay bereits aktiv an denselben Sachen gehackt hat und diese Änderung von Anfang an verfolgt hat, also ist es wahrscheinlich sinnvoll, dass sie fertig werden diese Änderung auch (ich glaube, sie haben bereits begonnen). Ich bin mir nicht sicher, ob es einen offensichtlichen Weg gibt, diese Bemühungen zu parallelisieren (sicherlich ist es möglich, aber wir müssten die Schritte vorher ein wenig ausspielen).

Wenn Sie jedoch daran interessiert sind, etwas zu finden, das Sie angehen können, kann ich empfehlen, einige der Fehler in diesem Meilenstein anzugehen? https://github.com/rust-lang/rust/issues/46472 scheint nicht ausgesprochen zu sein, ich kann versuchen dort bald ein paar Kommentare zu hinterlassen.

Sicherlich möchte ich niemandem auf die Füße treten, aber ich habe nichts gesehen, was darauf hindeutet, dass tatsächlich weitere Fortschritte erzielt wurden (obwohl zugegebenermaßen der Kommentar zu den nächsten Schritten ziemlich neu ist). Wirklich, ich habe angefangen zu versuchen, das zu lösen, weil ich in den letzten Tagen mehrmals GATs haben wollte, also würde es mir nicht unbedingt etwas ausmachen, in Zukunft ein paar NLL-Sachen durchzuarbeiten, aber das steht weit oben auf meiner Liste der zu lösenden Dinge im Augenblick.

@sunjay , wenn Sie immer noch aktiv daran arbeiten / planen, lassen Sie es mich bitte wissen - es hat keinen Sinn, dass ich Ihre Arbeit daran dupliziere.

Hallo @brandonson , danke für deinen Eifer und deine Hilfsbereitschaft. :) Ich arbeite tatsächlich aktiv daran. Ich bin jeden Teil der Implementierung Schritt für Schritt durchgegangen und habe eng mit Niko zusammengearbeitet, um alles richtig zu machen.

Ich werde alles tun, um das so schnell wie möglich herauszubringen. Ich will diese Funktion auch unbedingt!

Wie geht der Fortschritt? =) Ich kann es kaum erwarten, damit in Nightly <3 zu experimentieren

Ich frage mich, wie der Querschnitt von GAT und konstanten Generika funktionieren wird und ob er Teil der Vorschläge war, als er zusammengestellt wurde?

Ein Beispiel was ich meine:

trait Foo {
    type Bar<const N>;
}

Hallo @sunjay , ich denke, das ist ein ziemlich wichtiges Feature, es wurde in den Roadmap-Kommentaren 2018 stark erwähnt. Wie geht es voran? Danke für deine Arbeit!

Dies ist im Moment mein am meisten gewünschtes Feature. Ich hoffe, dass dies eine Priorität wird und bald seinen Weg in Nightly findet!

Also habe ich mich kürzlich mit @sunjay getroffen , der gerade mit der Schule und anderen Dingen beschäftigt ist, um zu versuchen, die nächsten Schritte hier festzulegen. Sie und ich hatten uns irgendwann getroffen und die allgemeine Implementierungsstrategie besprochen, was in einem Commit für ihr Repo gipfelte, in dem wir eine Reihe von Inline-Kommentaren hinterlassen hatten .

Ich denke, die Strategie, die für die Zukunft am sinnvollsten ist, ist eine zweifache:

  • Zuerst müssen wir einige weitere Tests aufschreiben.
  • Es gibt einige bekannte Mängel in unserem Parser und einigen anderen "Front-End"-Bits des Systems, die wir aufzählen und beheben müssen. Wir werden wahrscheinlich mehr in Tests finden.
  • An diesem Punkt sind wir bereit, das eigentliche Eigenschaftssystem zu zerhacken:

    • Ein Teil des Fundaments wurde bereits gelegt, daher ist dies hoffentlich weitgehend eine "Refaktorisierungs"-Aufgabe

    • aber ich muss im Detail aufschreiben, es gibt keine schriftliche Beschreibung des Plans, die ich kenne, und ich habe in dieser Minute keine Zeit dafür.

Ich beginne mit dem Versuch, einige Anweisungen zu Tests zu schreiben, da diese direkter umsetzbar sind, und plane später in dieser Woche etwas Zeit ein, um aufzuschreiben, wie der Rest des Designs funktionieren wird und wie man den Code von dort erhält, wo er ist jetzt wo es sein muss.

das ist wunderbar !! Viel Glück an @sunjay bei diesem und allen anderen Bemühungen.

Der erste Schritt wird sein, sicherzustellen, dass wir über eine vollständige Reihe von Tests verfügen. Bestehende Tests finden Sie in:

src/test/ui/rfc1598-generic-associated-types

Wenn wir uns nur diese ansehen, können wir bereits einige der zu erledigenden Arbeiten erkennen:

  • [ ] construct_with_other_type.rs -- gibt unerwarteten E0110-Fehler
  • [x] empty_generics -- prüft, ob type Bar<,> einen Fehler ausgibt, scheint ok zu sein
  • [x] generic-associated-types-where.rs – Prüft, ob wir where -Klauseln an den richtigen Stellen parsen können, scheint in Ordnung zu sein
  • [ ] generic_associated_type_undeclared_lifetimes.rs -- gibt unerwarteten E0110-Fehler
  • [ ] iterable.rs -- gibt unerwarteten E0110-Fehler
  • [ ] pointer_family.rs -- gibt unerwarteten Fehler E0109
  • [ ] streaming_iterator.rs -- gibt unerwarteten E0110-Fehler

Orte, die nicht abgedeckt sind

  • [ ] Wir haben derzeit nicht viele Tests zur „erwarteten Nutzung“ – dh Dinge, die erfolgreich sein sollten

    • pointer_family scheint in diese Richtung zu gehen

    • Ich würde eine Art trait Iterable { type Item; type Iter<'a>: Iterator<Item = &'a Self::Item>; } Test erwarten

  • [ ] Lifetime-Shadowing-Tests – wir verbieten generell Lifetime-Shadowing, also sollten diese illegal sein:

    • trait Foo<'a> { type Item<'a>; }

    • impl<'a> Foo<'a> for &'a u32 { type Item<'a> = i32; }

  • [ ] „vollständig qualifizierte“ Syntax scheint nicht getestet zu sein

    • zB hat der Test pointer_family Self::Pointer<T> , aber nicht <Self as PointerFamily>::Pointer<T>

  • [ ] Falsche Anzahl von Argumenten für ein GAT. zB angesichts der Iterable -Definition oben:

    • <T as Iterable>::Item - überhaupt keine Parameter? Schlecht.

    • Beachten Sie, dass wir dies in einigen Kontexten akzeptieren könnten , da wir in einigen vergleichbaren Kontexten das Eliding von Lebenszeiten zulassen.

      Ich könnte hier in beide Richtungen gehen; Ich würde es vorziehen, wenn die Leute in solchen Fällen explizit '_ schreiben würden.

    • <T as Iterable>::Item<'_> -- Richtig!

    • <T as Iterable>::Item<T> -- Zu viele Typen!

    • usw., wäre es gut, Tests zu haben, die natürlich sowohl Typen als auch Lebensdauern erfordern

  • [ ] Standardwerte für zugeordnete Typen? trait Foo { type Bar<T, U = T> where T: PartialEq<U>; }

    • in diesem Fall wäre SomeType::Bar<u32> shor für SomeType::Bar<u32,u32> , was wir überprüfen sollten.

Behebung unerwarteter E0110-Fehler

Fehler E0110 wird von prohibit_type_params gemeldet:

https://github.com/rust-lang/rust/blob/e65547d4fad0425d1db4f33a4d8134bf2cad939e/src/librustc_typeck/astconv.rs#L912

Der erste Schritt besteht darin, herauszufinden, woher das aufgerufen wird. Mein bevorzugter Weg, dies zu tun, besteht darin, einen lokalen Build zu erhalten und -Ztreat-err-as-bug in Kombination mit RUST_BACKTRACE=1 zu verwenden. Aber ich kann Ihnen diese Ergebnisse nicht zeigen, weil rustc noch aufgebaut wird. :P Also habe ich stattdessen schnell rg prohibit_type_params , lass mich schnell auf einen verdächtigen Fall hinweisen, den ich sehe.

Ein Aufruf ist von associated_path_def_to_ty :

https://github.com/rust-lang/rust/blob/e65547d4fad0425d1db4f33a4d8134bf2cad939e/src/librustc_typeck/astconv.rs#L791 -L803

Wie der Kommentar andeutet, wird dies aufgerufen, um die Komponente Pointer<T> in einem Pfad wie Self::Pointer<T> aufzulösen (beachten Sie, dass Typparameter zusammen mit dem Namen, an den sie angehängt sind, als Teil eines Pfadsegments betrachtet werden ). Bis GATs waren Typparameter dort nicht erlaubt (z. B. T::Item ), also haben wir nur eine pauschale Einschränkung:

https://github.com/rust-lang/rust/blob/e65547d4fad0425d1db4f33a4d8134bf2cad939e/src/librustc_typeck/astconv.rs#L810

Das geht natürlich nicht. Wir sollten diese Zeile entfernen und durch eine Art Überprüfung ersetzen, ob, wenn Parameter bereitgestellt werden, sie mit der erwarteten Zahl übereinstimmen. Dazu benötigen wir vermutlich einen Code zur Fehlerprüfung, der dem ähnelt, der in create_substs_for_ast_path zu finden ist. Wir möchten hier wahrscheinlich gemeinsam genutzten Code erstellen, insbesondere für die Buchhaltung mit Standardwerten - vielleicht können wir diese Funktion tatsächlich wiederverwenden?

Arbeitet noch jemand daran? Mir scheint, dass dies noch ein langer Weg ist. GAT ist mein begehrtester RFC. Wenn nicht, würde ich gerne ein paar Tests beisteuern ...

@rickyhan , also @Centril und @gavento sprachen im WG-Traits-Gitter über die Aufteilung der Testarbeit, aber ich weiß nicht, ob Fortschritte erzielt wurden. Vielleicht können sie sich einschalten. Ich denke, eine PR wäre willkommen. =)

Entschuldigung, wenn ich mich dumm ausdrücke, aber wird so etwas mit GATs legal sein?

trait Sequencer {
    type Wrap<A>;
    fn chain<A, B, F>(Self::Wrap<A>, F) -> Self::Wrap<B>
        where F: FnOnce(A) -> Self::Wrap<B>;
    fn wrap<A>(A) -> Self::Wrap<A>;
}

Wie ist der Stand davon? Ich weiß, dass Chalk kürzlich Gat-Unterstützung erhalten hat. Soll das bald im Rost landen?

@mark-im Ich habe es letzte Woche versucht. Nach meinem Verständnis ist der Syntaxparser da (obwohl Tests fehlen). Aber die "Implementierung" ist noch nicht geschrieben. (Siehe https://github.com/rust-lang/rust/issues/44265#issuecomment-330915766 für weitere Details)

@quadrupleslap AIUI, das wird später möglich sein, aber zunächst werden GATs nur Lebensdauerparameter unterstützen.

@Boscop der RFC gibt an, dass auch Typparameter unterstützt werden.

Kennt jemand den genauen Stand der Umsetzung der Syntax in rustc? Es scheint meistens da zu sein, aber höherrangige Grenzen erzeugen ICEs:
http://play.rust-lang.org/?gist=a48959858ed5dd432c2396feae5c3cc1&version=nightly&mode=debug

Ich müsste zumindest die gesamte Syntax implementieren, um mit der Kalkifizierungsarbeit voranzukommen. Wenn noch jemand an der Syntax arbeitet, lass es mich bitte wissen, sonst kann ich es selbst beheben :)

Für mich sieht es so aus, als ob die Kommunikation das Hauptproblem ist, das die Umsetzung von GATs verlangsamt. Es scheint zumindest ein paar Interessenten zu geben, daran mitzuarbeiten, aber keiner kennt wirklich den genauen Umsetzungsstand und wer schon daran arbeitet. Was halten Sie von einem Discord- (oder IRC?) Kanal, nur um über die Implementierung von GATs zu sprechen? Ich denke, das würde sicherlich helfen.

Außerdem könnte ich sicherlich in den nächsten Wochen ein paar Arbeitstage dazu beitragen, daran zu arbeiten (aber ich weiß noch nichts über diesen Teil des Compilers).

Also bat ich um einen eigenen Kanal auf Discord. Aber anstatt einen Kanal zu bekommen, habe ich ein paar Dinge gelernt. Ich habe auch nach allem gesucht, was mit GATs zu tun hat. Daher werde ich versuchen, alle Informationen zu dieser Funktion zusammenzufassen. vielleicht ist es für einige Leute nützlich. Aber ich weiß nichts, also nimm das mit einem Körnchen Salz! Außerdem: Bitte teilen Sie mir mit, wenn einige Informationen falsch sind, damit ich sie korrigieren kann.

Zusammenfassung der Umsetzungsbemühungen bezüglich GATs

Seitdem wurden keine UI-Tests mehr hinzugefügt . Und ich kann keine anderen PRs finden, die direkt mit GATs zu tun haben.

An dem Punkt (c) von oben (dem Trait-System) wird jedoch "im Geheimen" gearbeitet. Soweit ich weiß, ist geplant, bald auf den neuen Trait-Solver auf Kreidebasis zu migrieren und GATs nicht auf dem alten System zum Laufen zu bringen. Die Integration des neuen Trait-Solvers verfolgt das Tracking-Problem „Chalkification“ . Es gab einige PRs im Zusammenhang mit Kreide und Verkalkung. Insbesondere gibt es eine Kreide-PR mit dem Titel „Finish implementing GATs“ (zusammengeführt am 24.05.2018). Es scheint also, als wäre das Kernsystem für GATs bereits vorhanden.

Allerdings sind die "GATs" in Chalk eine Prototypimplementierung und die Verwendung in Rustc ist nicht nur ein use chalk; . Wie @scalexm mir sagte: „Es scheint ziemlich viel [zu tun] zu geben“.


Für weitere Informationen und um zu helfen, ist es wahrscheinlich nützlich, einen Blick in den #wg-traits Discord-Kanal und das Traits WG-Tracking-Problem zu werfen .

Also hat @nikomatsakis gerade den #wg-traits-gat -Kanal auf dem Rust-lang-Discord-Server erstellt ( hier beitreten ). Es wäre toll, wenn wir alle, die mithelfen wollen, mitnehmen könnten. Plus ein paar Leute, die über den Status dieses Features Bescheid wissen (insbesondere, was noch zu tun ist und wo wir helfen können). Das sollte die Kommunikation einfacher und schneller machen, besonders für Leute wie mich, die noch nicht tief involviert / Teil der Traits WG sind :)

Gibt es dazu in letzter Zeit ein Update? Warten wir auf die Chalk-Integration in den Compiler? (Vielleicht gibt es dafür ein eigenes Thema.)

Vielleicht gibt es dafür ein eigenes Thema.

48049

Nur als Hinweis (möglicherweise bekannt), versetzt dieser Code den Compiler in Panik:

use typenum::{U1,U2,U3,Unsigned};

trait Array {
    type Of<Elem> ;
}

impl Array for U1 { type Of<T> = [T;1]; }
impl Array for U2 { type Of<T> = [T;2]; }
impl Array for U3 { type Of<T> = [T;3]; }

@wdanilo : Ich glaube, das ist https://github.com/rust-lang/rust/issues/64755.

@varkor super , danke!

Noch eine Anmerkung (wieder, möglicherweise bekannt). Mit GATs wären wir in der Lage, die Typen & und &mut gut zu abstrahieren, sodass wir das ständige Kopieren und Einfügen von Code zwischen den Funktionen my_func und my_func_mut stoppen könnten

#![feature(arbitrary_self_types)]
#![feature(generic_associated_types)]

use std::marker::PhantomData;

struct Pure <'t> (PhantomData<&'t()>);
struct Mut  <'t> (PhantomData<&'t()>);

type Ref<M, T> = <M as Acc>::Pat<T>;

trait Acc { type Pat<T: ?Sized>; }
impl<'t> Acc for Pure <'t> { type Pat<T> = PureRef <'t, T>; }
impl<'t> Acc for Mut  <'t> { type Pat<T> = MutRef  <'t, T>; }

struct PureRef <'t, T: ?Sized> (&'t     T);
struct MutRef  <'t, T: ?Sized> (&'t mut T);


/// USAGE ///

struct Buf<T> {
    data: Vec<T>
}

impl<T> Buf<T> {
    fn as_mut<M: Acc>(s: Ref<M, Self>) -> Ref<M, [f32]>
    {
        unimplemented!()
    }
}

Es kompiliert fast . Wir können es auch ohne GATs implementieren, aber die Typen explodieren:

#![feature(arbitrary_self_types)]

use std::marker::PhantomData;
use std::ops::Deref;

struct Pure <'t> (PhantomData<&'t()>);
struct Mut  <'t> (PhantomData<&'t()>);

type Ref<M, T> = <M as Acc<T>>::Pat;

trait Acc<T: ?Sized> { type Pat; }
impl<'t, T: 't + ?Sized> Acc<T> for Pure <'t> { type Pat = PureRef <'t, T>; }
impl<'t, T: 't + ?Sized> Acc<T> for Mut  <'t> { type Pat = MutRef  <'t, T>; }

struct PureRef <'t, T: ?Sized> (&'t     T);
struct MutRef  <'t, T: ?Sized> (&'t mut T);


/// USAGE ///

struct Buf<T> {
    data: Vec<T>
}

impl<T> Buf<T> {
    fn as_mut<M>(self: Ref<M, Self>) -> Ref<M, [f32]>
    where M: Acc<Self> + Acc<[f32]>,
          Ref<M, Self>: Deref<Target = Self>
    {
        unimplemented!()
    }
}

(das kompiliert tatsächlich)

Vielleicht ist dies eher eine Support-Frage, aber ich denke, es könnte für diejenigen, die auf diese Seite kommen, nützlich sein, diesen Vorschlag zu verstehen, da es mir aus dem RFC oder den Kommentaren hier nicht ganz klar war:

Mit der 1.41 Nightly habe ich folgendes probiert:

pub trait MyTrait {
    type MyType<U>;

    fn f<U>(self, x : <Self as MyTrait>::MyType<U>);
}

Und dies kann nicht kompiliert werden, der Fehler lautet "Typargument nicht zulässig" MyType .

Ich habe dann das <U> entfernt, was verdächtig aussah, aber ich dachte, ich würde es versuchen:

pub trait MyTrait {
    type MyType<U>;

    fn f<U>(self, x : <Self as MyTrait>::MyType);
}

Was überraschenderweise kompiliert wurde. Aber dann, als ich ein Impl schrieb:

impl MyTrait for u64 {
    type MyType<U> = U;

    fn f<U>(self, x : <Self as MyTrait>::MyType) -> <Self as MyTrait>::MyType {
        x;
    }
}

Dies versetzte den Compiler in Panik.

Meine Fragen sind:

  1. Ist diese Art von Schema jetzt möglich, aber ich mache es nur falsch?
  2. Wenn es jetzt nicht möglich ist, wird es dann jemals unter diesem Vorschlag möglich sein?
  3. Wenn ja, gibt es eine Vorstellung von der Art von Zeitleiste, in der ich dies jede Nacht tun könnte?

Vielen Dank im Voraus für Ihre Zeit bei der Beantwortung dieser Frage.

@clintonmead Ich glaube, das sollte irgendwann möglich sein, aber die Feature-Implementierung ist noch nicht abgeschlossen (tatsächlich warnt Sie der Compiler, dass er abstürzen kann, wenn Sie versuchen, ihn zu verwenden!). Ich weiß nicht, wie der Zeitrahmen ist.

Vielleicht lohnt es sich, bei den Chalk-Jungs nachzusehen, ob die Integration weit genug fortgeschritten ist, um die Arbeit an dieser Funktion fortzusetzen?

Das ist jetzt gesperrt

  • #30472
  • #67509
  • #67510
  • #67512
  • #67513

Können wir die Problembeschreibung mit den aktuellen Blockern aktualisieren?

Weitere Blockierungsprobleme hinzugefügt, die von @DutchGhost aufgeworfen wurden

Dies wäre ein wichtiger Schritt in der Nachahmung von höherwertigen Arten.

Ich stelle mir sowas vor:

// the plug/unplug idea is from https://gist.github.com/edmundsmith/855fcf0cb35dd467c29a9350481f0ecf

trait Monad /* : Applicative (for pure/return, doesn't matter for this example) */ {
    // Self is like the "f a" in haskell

    /// extract the "a" from "f a"
    type Unplug;

    /// exchange the "a" in "f a" in the type of Self with B
    type Plug<B>: Monad;

    fn bind<B, F>(this: Self, f: F) -> Self::Plug<B>
    where
        F: Fn(Self::Unplug) -> Self::Plug<B>;
}

impl<A> Monad for Option<A> {
    type Unplug = A;
    type Plug<B> = Option<B>;
    fn bind<B, F>(this: Self, f: F) -> Option<B>
    where
        F: Fn(A) -> Option<B> {
        this.and_then(f)
    }
}

Frage zur vorgeschlagenen Umsetzung:

Ich habe eine Eigenschaft, die so aussieht

trait TradeableResource{
}

und ein Implementierer, der so aussieht

struct Food(f64);
impl TradeableResource for Food{}

Ich möchte in der Lage sein, alle Implementierer meiner Eigenschaft auf einen "Newtype" zu beschränken (Einzelelement-Tupelstruktur, die ein f64 umschließt).

Wäre das möglich, wenn die vorgeschlagene Implementierung hier in Betracht gezogen wird?

Das Folgende sieht zugegebenermaßen etwas seltsam aus, zeigt aber hoffentlich, was ich können möchte.

trait TradeableResource{
    type Wrapper<T>:T(f64)
}

@ChechyLevas , soweit mir bekannt ist, fällt dies nicht in den Geltungsbereich von GADTs.

Aber was Sie tun möchten, braucht, soweit ich das beurteilen kann, zunächst keine GADTs, wenn Sie bereit sind, die Garantie aufzugeben, dass es sich um einen neuen Typ handelt, und stattdessen Funktionen haben müssen, die den inneren Wert einschließen bzw. abrufen . Es ist nicht so bequem, sollte aber wahrscheinlich gut genug funktionieren.

Sie könnten also Folgendes tun:

trait Traceable resource: From<f64> + Into<f64> { }

(Dazu müssten Sie auch From in beide Richtungen implementieren, was ich über ein Makro tun würde.)

Ich würde gerne ein bisschen ein Gefühl für den aktuellen Stand bekommen. Ich habe ein bisschen mit asynchronen Merkmalen und Lebenszeiten gespielt.
Eine Sache, die ich tun wollte, war, ein trait ReadAt<'r> mit einem zugehörigen ReadAt: Future -Typ zu erstellen. Was in vielen Fällen funktioniert, außer wenn ich Eigenschaftsobjekte verwenden möchte.

Hauptsächlich, weil mich das Folgende zwingen würde, R eine nicht generische Lebensdauer anzuhängen:

impl<'a, 'r, R> ReadAt<'r> for &'a dyn for<'z> ReadAt<'z, ReadAt = R> + 'a {
    type ReadAt = R; // cannot have an `impl<R<'lifetime>>`
    ...
}

Also dachte ich, vielleicht könnte dies mit GATs gelöst werden, aber ich stieß auf ähnliche Probleme, bei denen ich wieder etwas Syntaxmagie bräuchte, weil Merkmalsobjekte das Ausschreiben zugehöriger Typen erfordern:

Wie:

trait ReadAt {
    type ReadAt<'r>: Future<Output = io::Result<usize>>;

    fn read_at<'a>(&'a self, buf: &'a mut [u8], at: u64) -> Self::ReadAt<'a>;
}

impl<'d> ReadAt for &'d (dyn ReadAt<HERE> + 'd) {
}

Ich brauche eine Möglichkeit, die Lebensdauer in HERE . Dies wird beispielsweise akzeptiert, aber IMO würde nicht ausreichen, da R zu konkret ist:

impl<'d, R> ReadAt for &'d (dyn ReadAt<ReadAt = R> + 'd) {
    type ReadAt<'r> = R;

    fn read_at<'a>(&'a self, buf: &'a mut [u8], at: u64) -> Self::ReadAt<'a> {
        (**self).read_at(buf, at)
    }
}

Aber ich kann nicht wirklich testen, ob das trotzdem funktionieren würde, da ich nicht sicher bin, wie ich dies in ein Trait-Objekt umwandeln soll, da ich nicht weiß, wie ich die Lebenszeiten in das folgende Snippet bringen würde:

struct Test<T: ReadAt>(T);

impl<T: ReadAt> Test<T> {
    fn into_trait_object<'a>(&'a self) -> Test<&'a dyn ReadAt<ReadAt = T::ReadAt>> {
        todo!();
    }
}

Da T::ReadAt ein Leben lang dauert, was ich nicht bereitstellen kann, würde dies eine Syntaxerweiterung für dyn ReadAt<ReadAt<'r> = T::ReadAt<'r>> vermissen. (Oder für übereinstimmende Lebensdauerparameter könnte das obige Snippet meiner Meinung nach einfach funktionieren ;-) )

Da ich nur Lebensdauerparameter verwende, keine Typparameter, sollte dies meiner Meinung nach technisch möglich sein, es sei denn, Lebensdauerparameter können Vtables und Typgrößen irgendwie beeinflussen?

@jendrikw Ihr Code wird mit dem neuesten Nightly (1.46) kompiliert und ausgeführt. Ich habe einige Tests mit benutzerdefinierten Typen usw. durchgeführt, aber mir fehlt das fp-Wissen, um mehr zu überprüfen.

Heute habe ich versucht, GATs für etwas zu verwenden, bei dem ich dachte, dass ein Leben vielleicht nicht generisch genug wäre (Arbeitscode folgt; vollständige Codedatei ):

/// Helper trait for "stripping indention" from an object reference
pub trait AsUnindented<'ast> {
    type Output;

    /// Returns a reference to the unindented part of `Self`
    fn as_unindented(&'ast self) -> Self::Output;
}

impl<'ast, T: 'ast> AsUnindented<'ast> for Indented<T> {
    type Output = &'ast T;

    #[inline]
    fn as_unindented(&'ast self) -> &'ast T {
        &self.data
    }
}

impl<'ast, T: 'ast> AsUnindented<'ast> for crate::Block<T>
where
    T: AsUnindented<'ast> + 'ast,
{
    type Output = crate::View<'ast, T, fn(&'ast T) -> T::Output>;

    #[inline]
    fn as_unindented(&'ast self) -> Self::Output {
        crate::View::new(self, T::as_unindented)
    }
}

Dann habe ich versucht, GATs im folgenden Code zu verwenden:

pub trait AsUnindented {
    type Output<'ast>;

    /// Returns a reference to the unindented part of `Self`
    fn as_unindented<'ast>(&'ast self) -> Self::Output<'ast>;
}

impl<T> AsUnindented for Indented<T> {
    type Output<'ast> = &'ast T;

    #[inline]
    fn as_unindented<'ast>(&'ast self) -> &'ast T {
        &self.data
    }
}

impl<T> AsUnindented for crate::Block<T>
where
    T: AsUnindented,
{
    type Output<'ast> = crate::View<'ast, T, fn(&'ast T) -> T::Output<'ast>>;

    #[inline]
    fn as_unindented<'ast>(&'ast self) -> Self::Output<'ast> {
        crate::View::new(self, T::as_unindented)
    }
}

Das geht nicht. Es schlägt mit E0309 ( the parameter type 'T' may not live long enough ) für beide Trait-Implementierungen fehl. Ich bin mir nicht sicher, wie ich das beheben soll, oder ob ich einen Denkfehler habe. Der Compiler möchte, dass ich T auf impl Ebene eine Grenze anfüge, aber auf dieser Ebene gibt es keine Lebensdauer 'ast , und ich möchte T: 'static nicht einschränken for<'ast> T: 'ast funktioniert nicht).

Bearbeiten : Ich habe versucht, GATs auf einen anderen Teil meiner Codebasis anzuwenden, es gibt (vorerst) nur einen Fehler, aber dieser kann nicht einfach behoben werden, da er von einer Lösung für mein oben genanntes Problem abhängt.

Die Überlebensgrenze kann dem zugeordneten Typ hinzugefügt werden (mit Self: 'ast für die Version des Merkmals). Wie das aussehen sollte, zeigt dieser Test:
https://github.com/rust-lang/rust/blob/db4826dd6ca48663a0b4c5ab0681258999017c7d/src/test/ui/generic-associated-types/iterable.rs#L6 -L21

naja das geht nur teilweise...


Fehlermeldungen

error[E0309]: the parameter type `T` may not live long enough
  --> src/indention.rs:33:5
   |
33 |     type Output<'ast> where T: 'ast = &'ast T;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: consider adding an explicit lifetime bound `T: 'ast`...
   = note: ...so that the type `T` will meet its required lifetime bounds

error[E0309]: the parameter type `T` may not live long enough
  --> src/indention.rs:45:5
   |
45 |     type Output<'ast> where T: 'ast = crate::View<'ast, T, fn(&'ast T) -> T::Output<'ast>>;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: consider adding an explicit lifetime bound `T: 'ast`...
   = note: ...so that the type `T` will meet its required lifetime bounds

error[E0309]: the parameter type `T` may not live long enough
  --> src/indention.rs:59:5
   |
59 | /     type Output<'ast2>
60 | |         where
61 | |             T: 'ast2,
62 | |             O: 'ast2
63 | |         = crate::View<'ast2, T, crate::view::MapViewFn<F, fn(F::Output<'ast2>) -> O::Output<'ast2>>>;
   | |_____________________________________________________________________________________________________^
   |
   = help: consider adding an explicit lifetime bound `T: 'ast2`...
   = note: ...so that the type `T` will meet its required lifetime bounds

error[E0309]: the parameter type `O` may not live long enough
  --> src/indention.rs:59:5
   |
59 | /     type Output<'ast2>
60 | |         where
61 | |             T: 'ast2,
62 | |             O: 'ast2
63 | |         = crate::View<'ast2, T, crate::view::MapViewFn<F, fn(F::Output<'ast2>) -> O::Output<'ast2>>>;
   | |_____________________________________________________________________________________________________^
   |
   = help: consider adding an explicit lifetime bound `O: 'ast2`...
   = note: ...so that the type `O` will meet its required lifetime bounds

Es scheint, als würde der Code eine Stufe durchlaufen (zuvor gab es ähnliche Fehler, aber zwischen ihnen tauchte eine andere Fehlerkategorie auf, die nur aus E0107 s bestand) hm.

Bearbeiten : Ich habe die erste Anweisung verpasst ( where Self: 'ast ). Das Teil ist jetzt behoben. Ich versuche weiterzumachen.


zusätzlichen Kontext

ok, folgender ausschnitt:

impl<'ast, T, F, O> AsUnindented for crate::View<'ast, T, F>
where
    Self: Clone,
    F: crate::view::ViewFn<T, Output = &'ast O>,
    O: AsUnindented + 'ast,
{
    type Output<'ast2>
        where
            T: 'ast2,
        = crate::View<'ast, T, crate::view::MapViewFn<F, fn(&'ast O) -> O::Output<'ast>>>;

    #[inline]
    fn as_unindented<'ast2>(&'ast2 self) -> Self::Output<'ast2> {
        self.clone().map::<for<'x> fn(&'x O) -> O::Output<'x>, _>(O::as_unindented)
    }
}

Fehler mit:

error[E0631]: type mismatch in function arguments
  --> src/indention.rs:66:67
   |
66 |         self.clone().map::<for<'x> fn(&'x O) -> O::Output<'x>, _>(O::as_unindented)
   |                                                                   ^^^^^^^^^^^^^^^^
   |                                                                   |
   |                                                                   expected signature of `for<'x> fn(<F as view::ViewFn<T>>::Output<'x>) -> _`
   |                                                                   found signature of `for<'x> fn(&'x O) -> _`

und ich weiß, dass <F as view::ViewFn<T>>::Output<'x> ==> &'ast O und &'x O nicht gleich sind, aber ich weiß nicht, wie ich das beheben soll (der Compiler akzeptiert das gebundene F: for<'x> crate::view::ViewFn<T, Output = &'x O> nicht).
Die anderen versuchen Fehler mit:

error[E0582]: binding for associated type `Output` references lifetime `'x`, which does not appear in the trait input types
  --> src/indention.rs:56:39
   |
56 |     F: for<'x> crate::view::ViewFn<T, Output = &'x O>,
   |                                       ^^^^^^^^^^^^^^

Ich weiß nicht, wie man ein forall<'x> ausdrückt, das in einer Trait-Ausgabetypzuweisung gebunden ist, z

where
    F: crate::view::ViewFn<T, for<'x> Output<'x> = &'x O>,

analysiert nicht einmal ("erwartet eines von + , , , :: oder > , gefunden = ").

67510 Spuren können diese Grenze schreiben. Dieses Beispiel benötigt möglicherweise auch eine verzögerte Normalisierung (#60471).

Ist der Trait-Ausgabetyp von self.clone().map wirklich der Typ von O::as_unindented , dh der Argumenttyp von map
Ich weiß nicht, was crate::View ist, aber die Funktion map erwartet möglicherweise ein anderes Argument, daher die Typenabweichung.

@sighoya Ich habe das Repository in früheren Beiträgen verlinkt, hier ist ein direkter Link zum Impl von crate::view::ViewFn::map

Was sagt der Compiler dazu?:

where
    for<'x> F: crate::view::ViewFn<T, Output<'x> = &'x O>,

Es wird (noch) nicht analysiert.

@matthewjasper ,

Aus Rust Nomicon wird Folgendes analysiert:

where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8,

Liegt es an der Verwendung von Output<'x> = &'x 0> , dass es nicht geparst wird?

Ja, Output<'x>=... wird an dieser Position nicht geparst.

Ich habe ein create, grdf , das nicht mehr mit rustc 1.46.0-nightly kompiliert wird. Hat sich in letzter Zeit etwas am GAT geändert?

Mein Fall ist etwas seltsam, da ich einen Trick anwenden musste, damit es überhaupt funktioniert. Ich habe unbedingt ein Graph -Merkmal mit zugehörigen Iteratortypen. Damit es funktioniert, habe ich alle Iteratoren in einem anderen Merkmal platziert, Iter :

pub trait Iter<'a, T: 'a> {
    type Triples: Iterator<Item = Triple<&'a T>>;
    type Subjects: Iterator<Item = (&'a T, Self::Predicates)>;
    type Predicates: Iterator<Item = (&'a T, Self::Objects)>;
    type Objects: Iterator<Item = &'a T>;
}

pub trait Graph<T = crate::Term> {
    /// Iterators.
    type Iter<'a>: Iter<'a, T>;

    ...
}

Ich habe eine Implementierung von Graph , die bis jetzt gut funktioniert hat:

impl<'a, T: 'a + Hash + Eq> crate::Iter<'a, T> for Iterators {
    type Objects = Objects<'a, T>;
    type Predicates = Predicates<'a, T>;
    type Subjects = Subjects<'a, T>;
    type Triples = Iter<'a, T>;
}

impl<T: Hash + Eq> crate::Graph<T> for HashGraph<T> {
    type Iter<'a> = Iterators;

    ...
}

Jetzt, wo ich rustc aktualisiert habe, schlägt es mit Folgendem fehl:

error[E0309]: the parameter type `T` may not live long enough
  --> src/hash_dataset.rs:50:2
   |
50 |     type Iter<'a> = Iterators;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: consider adding an explicit lifetime bound `T: 'a`...
   = note: ...so that the type `T` will meet its required lifetime bounds

Das macht für mich nicht viel Sinn, da T bereits in Iter an 'a gebunden ist ...

Okey, ich habe es wieder zum Laufen gebracht, indem ich einige where -Grenzen hinzugefügt habe.
In der Eigenschaft:

type Iter<'a>: Iter<'a, T> where T: 'a;

und in der Umsetzung:

type Iter<'a> where T: 'a = Iterators;

Aber ich bin mir nicht sicher, ob ich die genaue Semantik der where -Grenzen verstehe, insbesondere in der Implementierung (das erste Mal, dass ich das gesehen habe). Und auch, warum es vorher funktioniert hat.

BEARBEITEN: Ich konnte sogar meinen fiesen Hack entfernen und die zugehörigen Iteratoren in die Eigenschaft selbst einfügen.

Weitere Blockierungsprobleme hinzugefügt, die von @DutchGhost aufgeworfen wurden

Sie können auch https://github.com/rust-lang/rust/issues/74684 hinzufügen :)

Ich schreibe nicht gerne , bitte beeil dich schon , aber dieses Feature ist schon seit fast drei Jahren ins Stocken geraten, und ich glaube, dass es eines der begehrtesten neuen Features ist.

Wo sind wir damit? Die aktuellste Statuszusammenfassung hier ist von @LukasKalbertodt vom Juni 2018 . Wartet es auf "Verkalkung" ?

Naive Beobachtung: Fast alle meine gewünschten Anwendungen für GATs erfordern nur einen Lebensdauerparameter. Wäre eine abgespeckte Version von GATs nur auf Lebenszeit einfacher zu liefern?

https://github.com/rust-lang/rust/issues/44265#issuecomment -568247656 ist ein (etwas knappes) Update.

67510 ist das letzte große ICE/fehlende Feature, das implementiert werden muss.

Würde dieser RFC Monad und Functor direkt möglich machen? Oder gibt es noch mehr Arbeit, die an HKTs erledigt werden muss?

Würde dieser RFC Monad und Functor direkt möglich machen? Oder gibt es noch mehr Arbeit, die an HKTs erledigt werden muss?

@ibraheemdev Die RFC-Staaten

Dies fügt nicht alle Merkmale hinzu, die Menschen wünschen, wenn sie über höherwertige Typen sprechen. Zum Beispiel aktiviert es keine Eigenschaften wie Monad. Einige Leute ziehen es vielleicht vor, alle diese Funktionen auf einmal zu implementieren. Dieses Merkmal ist jedoch aufwärtskompatibel mit anderen Arten von höherwertigen Polymorphismen und schließt deren Implementierung in keiner Weise aus. Tatsächlich ebnet es den Weg, indem es einige Implementierungsdetails löst, die sich auch auf andere Arten höherer Art auswirken werden, wie z. B. die teilweise Anwendung.

@ibraheemdev : Mein Gefühl ist, dass der idiomatischste Weg, Monaden und Funktoren zu ermöglichen, darin besteht, generische assoziierte Merkmale einzuführen, aber generische assoziierte Typen sind sicherlich eine Voraussetzung.

Gibt es einen Pfad zu Monad , der keine GATs erfordert?

@ibraheemdev Hypothetisch ja, es wäre möglich, HKTs direkt zu implementieren und dann ein Monad -Merkmal darüber zu implementieren. Es gibt jedoch keine Arbeit in diese Richtung und wird es wahrscheinlich nie geben, da dieser Ansatz die Probleme von Rust nicht wirklich löst: https://twitter.com/withoutboats/status/1027702531361857536

Vielleicht irre ich mich, aber ich denke, GAT erlaubt so etwas

trait MonadFamily {
    type Monad<T>;
    fn pure<T>(inner: T) -> Self::Monad<T>;
    fn bind<T, U, F: FnOnce(T) -> U>(this: Self::Monad<T>, f: F) -> Self::Monad<U>;
}

@ibraheemdev

Gibt es einen Pfad zu Monad , der keine GATs erfordert?

Yup, siehe Method for Emulating Higher-Kinded Types in Rust . Es funktioniert jetzt sogar auf Stable.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen