Rust: 🔬 Problème de suivi des types génériques associés (GAT)

Créé le 2 sept. 2017  ·  67Commentaires  ·  Source: rust-lang/rust

Commentaire le plus utile

https://github.com/rust-lang/rust/issues/44265#issuecomment -568247656 est une mise à jour (quelque peu concise).

67510 est la dernière fonctionnalité majeure ICE/manquante qui doit être implémentée.

Tous les 67 commentaires

Voici une sorte de plan de mise en œuvre que je m'efforcerai de tenir à jour.

  • [ ] Première étape : ajouter la prise en charge dans l'AST et la jolie impression

    • La première étape consiste probablement à commencer à analyser les nouveaux formulaires et à ajouter leur prise en charge à l'AST.

    • Voir ce commentaire pour des réflexions plus détaillées ici .

    • Nous devrions pouvoir écrire des tests d'analyse uniquement et également tester la jolie imprimante hir

    • Lorsque nous arrivons à l' abaissement HIR , nous pouvons nous tromper si des GAT sont présents

    • Nous pouvons également faire la porte de fonctionnalité alors

  • [ ] Plus à venir

Permettez-moi de commencer par écrire plus en détail sur l'AST. Voyons d'abord comment cela fonctionne aujourd'hui :

Un élément type Foo: Bar [= Baz]; dans une définition de trait est défini par cette variante AST . Cela inclut les limites ( Bar ) et la valeur par défaut (facultative) Baz . Le nom est défini dans la structure TraitItem .

Un élément type Foo = Bar; dans un trait impl est défini par cette variante AST - qui n'inclut que le type Bar , car le Foo etc est défini dans le ImplItem structure .

Les méthodes sont un cas intéressant car elles peuvent déjà être rendues génériques. Ces paramètres génériques sont déclarés dans le champ Generics de la structure MethodSig . Ceci est une instance de la structure Generics .

Mon point de vue est que la meilleure chose à faire serait de "lever" Generics des méthodes dans les TraitItem (et ImplItem ) afin qu'elles s'appliquent également à toutes les formes de éléments de trait et d'impl. Pour l'instant, nous ne prendrons pas en charge les constantes génériques, je suppose, mais honnêtement, elles découlent probablement du travail que nous faisons dans tous les cas, donc ce serait une petite extension. Je pense que le travail ira mieux si nous les planifions maintenant.

Peut-être qu'un premier PR décent serait de faire ce changement , en gardant toutes les autres fonctionnalités existantes identiques. Autrement dit, nous aurions plus Generics dans TraitItem (et ImplItem ) et sur MethodSig . Nous fournirions un Generics vide pour les non-méthodes. Nous travaillerions sur le code existant, en associant les génériques au besoin pour le faire fonctionner.

@nikomatsakis Cool ! Merci beaucoup! J'ai commencé à expérimenter hier soir et je suis fier de dire que j'ai trouvé les mêmes endroits que vous avez indiqués dans votre commentaire sur l'AST. :sourire: (Étant donné que c'était ma première fois en rustc, je considère cela comme un accomplissement !)

Je n'ai pas pensé à élever les génériques dans TraitItem . Mon approche consistait à mettre Generics dans TraitItemKind::Type puisque c'est là que la déclaration de type est déjà stockée. Votre approche est également logique, je vais donc travailler à sa mise en œuvre. Étant donné que je suis encore complètement nouveau dans cette base de code, je suis intéressé de savoir quels seraient les pièges de mon approche si elle était utilisée par rapport à celle que vous avez suggérée. Pourriez-vous me donner un aperçu de votre processus de réflexion? :smiley:

Voici la modification que j'aurais faite :

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 : Réponse de nikomatsakis sur Gitter

concernant les pièges à les mettre en caractères
je pense que ça peut marcher aussi
la raison pour laquelle j'étais réticent à le faire
c'est qu'on veut vraiment faire les mêmes choses (en théorie, du moins) pour les méthodes et les types
et - comme je l'ai dit - en principe, je ne vois aucune raison pour laquelle nous ne pourrions pas faire la même chose pour les constantes
Je pense que si vous déplacez simplement les génériques vers la variante de type
cela fonctionnerait probablement bien, mais si vous regardez autour de vous, nous devons souvent faire "une chose pour les types/consts, une chose pour les méthodes" précisément parce qu'elles sont différentes
donc je soupçonne que le code deviendra plus uniforme
Je ne sais pas vraiment comment ça va se passer pour être honnête =) -- ça pourrait être pénible
mais la plupart du temps, faire en sorte que les choses soient plus génériques qu'elles ne doivent l'être n'est pas si mal, car vous pouvez insérer span_bug ! appels dans les cas impossibles pour l'instant (et plus tard, nous arrivons et les rafistolons)

D'accord! L'étape suivante consiste à étendre l'analyseur. Voici quelques conseils. Commençons par les éléments de trait.

Cette routine analyse les éléments de trait . Nous voulons étendre le cas qui gère les types associés pour analyser également des choses comme type Foo<....> = ...; (peut-être aussi des clauses where). (Le <...> est le "générique" que nous venons d'ajouter à l'AST.)

Actuellement, il utilise parse_ty_param , qui analyse essentiellement quelque chose comme T: Foo etc. Nous devrons arrêter de le faire, car la grammaire des déclarations de type associées ne correspond plus à celle des paramètres de type. Nous voudrons donc probablement ajouter quelque chose comme parse_trait_item_assoc_ty . Cela peut commencer comme une sorte de clone de parse_ty_param() , mais nous voudrons ensuite le modifier pour invoquer parse_generics() juste ici. Cette routine analysera une déclaration de génériques ( <...> ) si elle est présente, sinon elle renvoie simplement un générique vide. Ensuite, nous voulons ajouter un appel pour analyser les clauses where ici - vous pouvez modéliser cela sur l' appel qui se produit lors de l'analyse des méthodes , notez que le résultat est stocké dans le generics que nous avons analysé plus tôt.

Une fois que nous avons fait cela, nous devrions être en mesure d'ajouter quelques tests d'analyse. Je le ferais en créant un répertoire comme src/test/run-pass/rfc1598-generic-associated-types/ et en ajoutant des fichiers que vous vous attendez à analyser avec succès. Pour le moment, ils ne fonctionneront pas correctement, mais cela n'a pas d'importance. Ajoutez simplement une fonction principale vide. Ensuite, nous pouvons également ajouter des exemples qui ne doivent pas être analysés en src/test/ui/rfc1598-generic-associated-types/ (voir COMPILER_TESTS.md pour des instructions sur l'ajout de tests d'interface utilisateur ).

Quelque chose d'autre - nous devons présenter ce travail à ce stade, pour éviter que les gens n'utilisent ce matériel dans des versions stables. Il y a quelques instructions pour ajouter une porte de fonctionnalité ici sur forge (voir la dernière section). Nous devrions ajouter un visit_trait_item et visit_impl_item au visiteur dans feature_gate.rs ; si cet élément n'est pas une méthode, mais qu'il a un générique non vide, nous pouvons invoquer gate_feature_post ( exemple ).

Pour configurer la résolution de noms, je pense que tout ce que nous avons à faire est de mettre en place les "nervures" appropriées (les éléments de résolution de noms organisent les ensembles de noms concernés en nervures ; chaque nervure représente un niveau de liaison). par exemple pour un impl:

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

nous aurions les côtes suivantes :

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

En général, modéliser les choses sur le fonctionnement des méthodes n'est pas une mauvaise idée. Nous pourrions aussi faire un peu de "preuve future" ici, je suppose.

Voici le code qui apporte les paramètres de type d'une méthode dans la portée (c'est pour une méthode définie dans un trait):

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

Alors que pour un type défini dans un trait, nous sommes codés en dur pour ajouter un paramètre de type vide rib ( NoTypeParameters ) :

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

Maintenant que les génériques sont en place sur chaque élément trait/impl, je pense que nous voulons probablement supprimer la gestion de type et extraire la gestion de la méthode afin qu'elle se produise à un niveau supérieur. Pour les éléments (par exemple, const ) où il n'y a pas de génériques, la côte nouvellement introduite devrait être vide et donc inoffensive (j'espère).

Autres centres d'intérêt :

Vous avez eu l'idée.

@petrochenkov - ça sonne bien, n'est-ce pas ?

@nikomatsakis

sonner à droite?

Tout semble correct.

L'étape suivante. Résolution à vie.

Pour le meilleur ou pour le pire, cela se fait actuellement dans un morceau de code entièrement séparé des autres résolutions de noms. En effet, il a lieu après la construction du HIR. Cela va certainement changer, mais cela n'a pas encore changé.

Les idées de base sont les mêmes que dans la résolution de nom normale, sauf que nous n'appelons pas les choses "ribs" mais plutôt "scopes". =)

Il y a quelques complications bénignes à cause de ce concept de durée de vie « tardive ». Ce n'est pas vraiment pertinent ici cependant - toutes les durées de vie d'un type associé générique seront "liées tôt", ce qui est un peu le cas le plus simple. Une durée de vie "liée tardivement" est une durée de vie déclarée sur une méthode ou une fonction dont la valeur n'est pas fournie tant que la méthode n'est pas invoquée. Je n'entrerai pas dans les détails ici parce que ce n'est pas si pertinent -- l'essentiel est que nous ne voulons pas suivre exactement le même modèle pour les méthodes que nous le faisons pour d'autres types d'éléments génériques, contrairement à l'autre résolution de nom cas.

Voici un exemple de bout de code. Il s'agit du code qui visite un élément impl , struct ou un autre élément non fonctionnel. Dans ces cas, comme dans les GAT, nous souhaitons essentiellement intégrer tous les paramètres de durée de vie d'un Generics dans la portée et les mapper sur des durées de vie "précoces":

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

Vous pouvez voir qu'il crée d'abord un vecteur de durées de vie, appelant Region::early pour chacune :

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

Ensuite, il crée un Scope . La variable next_early_index compte simplement le nombre de durées de vie à liaison précoce comprises dans la portée. Étant donné que ce code est spécifique aux éléments, il s'agira toujours du nombre de durées de vie à liaison anticipée déclarées sur cet élément en cours. (Plus tard, nous examinerons un cas où nous apportons des durées de vie supplémentaires dans la portée, ce qui est plus ce que nous voulons pour les GAT.)

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

Enfin, nous invoquons with() . Cette méthode amènera la portée dans la portée et invoquera une fermeture. Toutes les durées de vie que nous visitons à l'intérieur de cette fermeture verront les noms que nous venons de définir comme étant dans la portée :

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

OK, regardons maintenant un autre exemple. Ce cas couvre les "traits impl". Les détails de ce qu'il fait ne sont pas si importants (c'est-à-dire que vous n'avez pas à le faire sous le impl Trait désucrage en soi). Il suffit de dire qu'il apporte de nouvelles durées de vie à liaison anticipée - c'est précisément ce que nous allons vouloir faire pour les GAT.

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

Permettez-moi de souligner quelques éléments. Tout d'abord, la méthode next_early_index renvoie le prochain index à liaison anticipée non affecté :

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

Cela fournit un point de départ. Ensuite, nous utilisons à nouveau Region::early pour créer de nouvelles définitions de durée de vie à liaison anticipée qui seront résolues :

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

Enfin, nous ramenons ceux-ci dans la portée en appelant à nouveau with :

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

OK, ce sont deux exemples. Nous allons vouloir faire quelque chose comme le second. Nous voudrons modifier les définitions de ces deux méthodes :

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

Les deux devront, pour les types associés, traiter les génériques associés. (Ils devraient probablement affirmer, dans les autres cas, que les génériques sont vides.)

J'ai donc sauté dessus plus tôt dans la journée. Je voudrais m'assurer d'être sur la bonne voie :

  • Tout ce qui devait être fait était d'introduire les durées de vie sur la carte, puis de parcourir l'élément trait/impl ? La vérification semble fonctionner, même si c'est difficile à dire (voir plus loin) ... peut ne fonctionner que dans le cas simple (illimité).
  • J'ai supprimé les interdictions de paramètre de type pour qpath_to_ty et associated_path_def_to_ty dans librustc_typeck/astconv.rs pour corriger les erreurs type parameters are not allowed on this type . Je pense que cela doit être remplacé par quelques vérifications. Également...
  • Je reçois des plantages de typeck maintenant. (réécriture, plus précisément)

les échecs de typeck sont déclenchés src/test/compile-fail/struct-path-associated-type.rs car il fournit des génériques aux valeurs qui n'ont pas de type associé.

Si je lis bien les choses, je dois au moins ajouter une vérification que les décomptes génériques associés correspondent (en essayant de savoir où faire cela...), et éventuellement faire d'autres vérifications pour ajouter des types pour les nœuds, etc.

Je vais travailler là-dessus, mais les indications pour savoir si je vais même dans la bonne direction sont appréciées.

Salut @brandonson ! Je déteste décourager quiconque de pirater le compilateur Rust, mais je pense que @sunjay piratait déjà activement les mêmes choses et a en quelque sorte poursuivi ce changement depuis le début, donc il est probablement logique qu'ils finissent ce changement aussi (je pense qu'ils ont déjà commencé). Je ne sais pas s'il existe un moyen évident de paralléliser cet effort (c'est certainement possible, mais nous devrions définir les étapes un peu à l'avance).

Cependant, si vous souhaitez trouver quelque chose à résoudre, puis-je vous recommander de vous attaquer à certains des bogues de cette étape ? https://github.com/rust-lang/rust/issues/46472 ne semble pas être parlé, je peux essayer d'y laisser quelques commentaires bientôt.

Certes, je ne veux pas marcher sur les pieds de qui que ce soit, mais je n'ai rien vu indiquant que de nouveaux progrès étaient en cours (bien qu'il soit vrai que le commentaire concernant les prochaines étapes est plutôt récent). Vraiment, j'ai commencé à essayer de résoudre ce problème parce que je voulais des GAT plusieurs fois au cours des deux derniers jours, donc même si cela ne me dérangerait pas nécessairement de travailler sur des trucs NLL à l'avenir, c'est bien plus haut dans ma liste de choses à résoudre en ce moment.

@sunjay , si vous travaillez ou envisagez toujours de travailler activement sur ce sujet, faites-le moi savoir - cela ne sert à rien de me faire dupliquer votre travail à ce sujet.

Salut @brandonson , merci pour votre empressement et votre volonté d'aider. :) J'y travaille effectivement activement. J'ai parcouru chaque partie de la mise en œuvre étape par étape tout en travaillant en étroite collaboration avec Niko pour bien faire les choses.

Je ferai tout ce que je peux pour le sortir au plus vite. Je veux vraiment cette fonctionnalité aussi!

Comment va la progression ? =) J'ai hâte d'expérimenter ça tous les soirs <3

Je me demande comment la coupe transversale des génériques GAT et const fonctionnera et si cela faisait partie des propositions lorsqu'elles ont été assemblées ?

Un exemple de ce que je veux dire :

trait Foo {
    type Bar<const N>;
}

Salut @sunjay , je pense que c'est une fonctionnalité assez importante, elle a été fortement mentionnée dans les commentaires de la feuille de route 2018. Comment ça progresse ? Merci pour votre travail !

C'est ma fonctionnalité la plus recherchée pour le moment. J'espère que cela deviendra une priorité et trouvera bientôt sa place dans Nightly !

Donc, récemment, j'ai rencontré @sunjay , qui est occupé en ce moment avec l'école et d'autres choses, pour essayer de présenter les prochaines étapes ici. Eux et moi nous nous sommes rencontrés à un moment donné et avons discuté de la stratégie globale de mise en œuvre, aboutissant à un commit sur leur repo où nous avons laissé un tas de commentaires en ligne .

Je pense que la stratégie qui a le plus de sens pour aller de l'avant est double :

  • Tout d'abord, nous devons rédiger d'autres tests.
  • Il y a quelques défauts connus dans notre analyseur et quelques autres éléments "frontaux" du système, nous devons les énumérer et les corriger. Nous en trouverons probablement plus dans les tests.
  • À ce stade, nous sommes prêts à commencer à pirater le système de traits proprement dit :

    • une partie des fondations a déjà été posée, il s'agit donc, espérons-le, en grande partie d'une tâche de "refactorisation"

    • mais j'ai besoin d'écrire en détail, il n'y a pas de description écrite du plan que je connaisse et je n'ai pas le temps pour ça cette minute.

Je vais commencer par essayer de rédiger quelques instructions concernant les tests, car ceux-ci sont plus immédiatement exploitables, et je programmerai un peu plus tard cette semaine pour écrire comment le reste de la conception fonctionnera et comment obtenir le code d'où il se trouve maintenant là où il doit être.

c'est merveilleux !! bonne chance à @sunjay dans cette entreprise et dans toutes les autres.

La première étape consistera à s'assurer que nous disposons d'une suite complète de tests. Les tests existants peuvent être trouvés dans :

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

Rien qu'en les regardant, on peut déjà voir une partie du travail à faire :

  • [ ] construct_with_other_type.rs - donne une erreur E0110 inattendue
  • [x] empty_generics -- vérifie que type Bar<,> donne une erreur, semble correct
  • [x] generic-associated-types-where.rs -- vérifie que nous pouvons analyser les clauses where aux bons endroits, semble correct
  • [ ] generic_associated_type_undeclared_lifetimes.rs - donne une erreur E0110 inattendue
  • [ ] iterable.rs - donne une erreur E0110 inattendue
  • [ ] pointer_family.rs - donne une erreur E0109 inattendue
  • [ ] streaming_iterator.rs - donne une erreur E0110 inattendue

Lieux qui manquent de couverture

  • [ ] nous n'avons actuellement pas beaucoup de tests "d'utilisation attendue" - c'est-à-dire des choses qui devraient réussir

    • pointer_family semble être dans cette direction

    • Je m'attendrais à une sorte de test trait Iterable { type Item; type Iter<'a>: Iterator<Item = &'a Self::Item>; }

  • [ ] tests d'observation à vie -- nous interdisons généralement l'observation à vie, donc ceux-ci devraient être illégaux :

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

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

  • [ ] La syntaxe "complètement qualifiée" ne semble pas avoir été testée

    • par exemple, le test pointer_family a Self::Pointer<T> , mais pas <Self as PointerFamily>::Pointer<T>

  • [ ] Mauvais nombre d'arguments à un GAT. par exemple, étant donné la définition Iterable ci-dessus :

    • <T as Iterable>::Item -- aucun paramètre du tout ? Mal.

    • Notez que nous pourrions accepter cela dans certains contextes, puisque dans certains contextes comparables, nous autorisons des durées de vie élidées.

      Je pourrais aller dans les deux sens ici; Je préférerais que les gens écrivent un '_ explicite dans des cas comme celui-ci.

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

    • <T as Iterable>::Item<T> -- Trop de types !

    • etc, ce serait bien d'avoir des tests qui prennent à la fois les types et les durées de vie bien sûr

  • [ ] Des valeurs par défaut sur les types associés ? trait Foo { type Bar<T, U = T> where T: PartialEq<U>; }

    • dans ce cas, SomeType::Bar<u32> serait court pour SomeType::Bar<u32,u32> , ce que nous devrions vérifier.

Correction des erreurs E0110 inattendues

L'erreur E0110 est signalée par prohibit_type_params :

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

La première étape consiste à déterminer d'où cela est invoqué. Ma façon préférée de le faire est d'obtenir une version locale et d'utiliser -Ztreat-err-as-bug combiné avec RUST_BACKTRACE=1 . Mais je ne peux pas vous montrer ces résultats car rustc est encore en construction. :P Donc, à la place, j'ai fait un rapide rg prohibit_type_params , permettez-moi de signaler rapidement un cas suspect que je vois.

Une invocation est de associated_path_def_to_ty :

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

Comme le commentaire l'indique, ceci est invoqué pour résoudre le composant Pointer<T> dans un chemin comme Self::Pointer<T> (notez que les paramètres de type sont considérés comme faisant partie d'un segment de chemin, avec le nom auquel ils sont attachés ). Jusqu'aux GAT, les paramètres de type n'y étaient pas légaux (par exemple, T::Item ), nous avons donc juste une restriction générale :

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

Clairement cela ne suffira pas. Nous devrions supprimer cette ligne et la remplacer par une sorte de vérification que, si des paramètres sont fournis, ils correspondent au nombre attendu. Pour ce faire, nous voulons probablement un code de vérification d'erreur similaire à celui trouvé dans create_substs_for_ast_path . Nous voulons probablement créer du code partagé ici, en particulier pour la comptabilité avec les valeurs par défaut -- peut-être pouvons-nous réellement réutiliser cette fonction ?

Est-ce que quelqu'un travaille encore là-dessus ? Il me semble que cela a un long chemin à parcourir. GAT est mon RFC le plus désiré. Sinon, j'aimerais contribuer à quelques tests...

@rickyhan donc @Centril et @gavento parlaient sur le gitter WG-traits de la division du travail de test, mais je ne sais pas si des progrès ont été réalisés. Peut-être qu'ils peuvent intervenir. Je pense qu'un PR serait le bienvenu. =)

Désolé si je suis stupide, mais est-ce que ce genre de chose va être légal avec les GAT ?

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>;
}

Où en est-on ? Je sais que la craie a récemment obtenu le soutien de gat. Est-ce destiné à atterrir dans la rouille bientôt?

@mark-im J'ai essayé la semaine dernière. D'après ce que j'ai compris, l'analyseur de syntaxe est là (bien qu'il manque des tests). Mais la "mise en œuvre" n'est pas encore écrite. (voir https://github.com/rust-lang/rust/issues/44265#issuecomment-330915766 pour plus de détails)

@quadrupleslap AIUI, cela sera possible plus tard, mais dans un premier temps, les GAT ne prendront en charge que les paramètres de durée de vie.

@Boscop la RFC spécifie que les paramètres de type seront également pris en charge.

Est-ce que quelqu'un connaît l'état exact de l'implémentation de la syntaxe dans rustc ? Cela semble principalement là, mais des limites de rang supérieur génèrent des ICE :
http://play.rust-lang.org/?gist=a48959858ed5dd432c2396feae5c3cc1&version=nightly&mode=debug

J'aurais au moins besoin que toute la syntaxe soit implémentée pour avancer dans le travail de craie. Si quelqu'un travaille encore sur la syntaxe, faites-le moi savoir, sinon je pourrais aller le réparer moi-même :)

Il me semble que la communication est le principal problème qui ralentit la mise en œuvre des GAT. Il semble y avoir au moins quelques personnes intéressées à travailler là-dessus, mais personne ne connaît vraiment l'état exact de la mise en œuvre et qui y travaille déjà. Que pensez-vous d' un canal Discord (ou IRC ?) Juste pour parler de la mise en œuvre des GAT ? Je pense que cela aiderait certainement.

De plus, je pourrais certainement contribuer quelques jours de travail dans les semaines à venir pour travailler dessus (mais je ne connais pas encore vraiment cette partie du compilateur).

J'ai donc demandé une chaîne dédiée sur Discord. Mais au lieu d'obtenir une chaîne, j'ai appris quelques choses. J'ai également recherché tout ce qui concernait les GAT. Je vais donc essayer de résumer toutes les informations concernant cette fonctionnalité ; c'est peut-être utile à certaines personnes. Mais je ne sais rien, alors prends ça avec un grain de sel ! Aussi : veuillez me dire si certaines informations sont incorrectes afin que je puisse les corriger.

Résumé des efforts de mise en œuvre concernant les GAT

Depuis lors , aucun test d'interface utilisateur n'a été ajouté . Et je ne trouve aucun autre PR directement lié aux GAT.

Cependant , le point (c) ci-dessus (le système de traits) est travaillé "en secret". Autant que je sache, le plan est de migrer bientôt vers le nouveau solveur de traits basé sur la craie et de ne pas faire fonctionner les GAT sur l'ancien système. L'intégration du nouveau solveur de traits est suivie par le problème de suivi "Chalkification" . Il y a eu pas mal de relations publiques liées à la craie et à la craie. Notamment, il existe un PR à la craie appelé "Terminer la mise en œuvre des GAT" (fusionné le 2018-05-24). Il semble donc que le système central des GAT soit déjà en place.

Cela dit, les "GAT" dans la craie sont une implémentation prototype et l'utiliser dans rustc n'est pas seulement un use chalk; . Comme me l'a dit @scalexm : "Il semble y avoir beaucoup [à faire]".


Pour plus d'informations et pour vous aider, il est probablement utile de jeter un œil au canal Discord #wg-traits et au problème de suivi des traits WG .

Donc @nikomatsakis vient de créer le canal #wg-traits-gat sur le serveur discord rust-lang ( rejoignez ici ). Ce serait formidable si nous pouvions amener tous ceux qui veulent aider là-bas. Plus quelques personnes qui connaissent l'état de cette fonctionnalité (en particulier, ce qu'il reste à faire et où nous pourrions aider). Cela devrait rendre la communication plus facile et plus rapide, en particulier pour les personnes comme moi qui ne sont pas encore profondément impliquées/qui ne font pas partie du groupe de travail sur les traits :)

Y a-t-il eu une mise à jour à ce sujet récemment ? Attendons-nous l'intégration de Chalk dans le compilateur ? (Peut-être y a-t-il un autre problème pour cela.)

Il y a peut-être un autre problème à cela.

48049

Juste comme une note (éventuellement connue), ce code affole le compilateur :

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 : Je crois que c'est https://github.com/rust-lang/rust/issues/64755.

@varkor génial, merci!

Encore une autre note (encore une fois, peut-être connue). Avec les GAT en place, nous serions en mesure d'abstraire bien les types & et &mut , de sorte que nous pourrions arrêter constamment de copier-coller du code entre les fonctions my_func et my_func_mut :) Voici une implémentation possible d'une telle abstraction (alias pseudo HKT) :

#![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!()
    }
}

Il compile presque . On peut aussi l'implémenter sans GAT mais les types explosent :

#![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!()
    }
}

(cela compile en fait)

Il s'agit peut-être davantage d'une question d'assistance, mais je pense qu'il pourrait être utile à ceux qui viennent sur cette page de comprendre cette proposition, car elle n'était pas tout à fait claire pour moi à partir de la RFC ou des commentaires ici :

Avec le 1.41 nightly, j'ai essayé ce qui suit :

pub trait MyTrait {
    type MyType<U>;

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

Et cela ne se compile pas, l'erreur étant "argument de type non autorisé" MyType .

J'ai ensuite supprimé le <U> , qui avait l'air suspect mais j'ai pensé que j'allais essayer:

pub trait MyTrait {
    type MyType<U>;

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

Qui a étonnamment compilé. Mais ensuite, quand j'ai écrit un impl:

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

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

Cela a paniqué le compilateur.

Mes questions sont :

  1. Est-ce que ce genre de schéma est possible maintenant, mais je le fais juste mal?
  2. Si ce n'est pas possible maintenant, le sera-t-il un jour dans le cadre de cette proposition?
  3. Si oui, y a-t-il une idée du type de calendrier dans lequel je serais capable de le faire tous les soirs ?

Merci d'avance pour le temps que vous consacrerez à cette réponse.

@clintonmead Je pense que cela devrait éventuellement être possible, mais l'implémentation de la fonctionnalité n'est pas encore terminée (en effet, le compilateur vous avertit qu'il peut planter lorsque vous essayez de l'utiliser !). Je ne sais pas quel est le délai.

Peut-être vaut-il la peine de vérifier avec les gars de Chalk pour voir si l'intégration est suffisamment avancée pour que le travail sur cette fonctionnalité reprenne ?

Ceci est maintenant bloqué sur

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

Pouvons-nous mettre à jour la description du problème avec les bloqueurs actuels ?

Ajout d'autres problèmes de blocage soulevés par @DutchGhost

Ce serait une étape majeure dans l'émulation des types de type supérieur.

J'imagine quelque chose comme ça :

// 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)
    }
}

Question sur la mise en œuvre proposée :

J'ai un trait qui ressemble à ça

trait TradeableResource{
}

et un implémenteur qui ressemble à ceci

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

J'aimerais pouvoir limiter tous les implémenteurs de mon trait à un "nouveau type" (structure de tuple à élément unique enveloppant un f64).

Cela serait-il possible avec la mise en œuvre proposée envisagée ici ?

Ce qui suit semble un peu bizarre certes, mais j'espère qu'il démontre ce que je veux être capable de faire.

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

@ChechyLevas , pour autant que je sache, cela n'entre pas dans le cadre des GADT.

Mais ce que vous voulez faire n'a pas besoin de GADT pour commencer, d'après ce que je peux comprendre, si vous êtes prêt à renoncer à la garantie qu'il s'agit d'un nouveau type et à la place à des fonctions qui enveloppent et récupèrent la valeur interne, respectivement . Ce n'est pas aussi pratique, mais devrait probablement fonctionner assez bien.

Ainsi, vous pourriez faire ce qui suit :

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

(cela nécessiterait que vous implémentiez également From dans les deux sens, ce que je ferais via une macro)

J'aimerais avoir une idée de l'état actuel de la situation. J'ai un peu joué avec les traits asynchrones et les durées de vie.
Une chose que je voulais faire était de créer un trait ReadAt<'r> avec un type ReadAt: Future associé. Ce qui fonctionne dans de nombreux cas, sauf lorsque je souhaite utiliser des objets de trait.

Principalement parce que faire ce qui suit m'obligerait à attacher une durée de vie pas assez générique à R :

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>>`
    ...
}

J'ai donc pensé que cela pourrait peut-être être résolu avec les GAT, mais j'ai rencontré des problèmes similaires où j'aurais à nouveau besoin de magie de syntaxe, car les objets de trait nécessitent que les types associés soient écrits:

Comme:

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) {
}

J'aurais besoin d'un moyen d'inclure la durée de vie dans HERE . Ceci, par exemple, est accepté, mais IMO ne suffirait pas, car R est trop concret :

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)
    }
}

Mais je ne peux pas vraiment tester si cela fonctionnerait de toute façon, car je ne sais pas comment transformer cela en un objet de trait, car je ne sais pas comment j'obtiendrais les durées de vie dans l'extrait suivant :

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!();
    }
}

Étant donné que T::ReadAt prend une durée de vie que je ne peux pas fournir, il manquerait donc une extension de syntaxe pour dyn ReadAt<ReadAt<'r> = T::ReadAt<'r>> . (Ou pour faire correspondre les paramètres de durée de vie IMO, l'extrait ci-dessus pourrait fonctionner ;-) )

Étant donné que j'utilise uniquement des paramètres de durée de vie, pas des paramètres de type, je pense que cela devrait être techniquement possible, à moins que les paramètres de durée de vie puissent affecter les vtables et les tailles de type d'une manière ou d'une autre?

@jendrikw votre code compile et s'exécute avec la dernière version nocturne (1.46). J'ai fait quelques tests avec des types personnalisés, etc. mais je n'ai pas les connaissances en fp pour en vérifier davantage.

Aujourd'hui, j'ai essayé d'utiliser les GAT pour quelque chose où je pensais qu'une vie ne serait peut-être pas assez générique (le code de travail suit ; fichier de code complet ):

/// 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)
    }
}

Ensuite, j'ai essayé d'utiliser les GAT dans le code suivant :

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)
    }
}

Cela ne fonctionne pas. Il échoue avec E0309 ( the parameter type 'T' may not live long enough ) pour les deux implémentations de trait. Je ne sais pas comment résoudre ce problème ou si j'ai une idée fausse. Le compilateur veut que j'attache une limite sur T au niveau impl , mais à ce niveau, il n'y a pas de durée de vie 'ast , et je ne veux pas contraindre T: 'static (et quelque chose comme for<'ast> T: 'ast ne fonctionne pas).

edit : j'ai essayé d'appliquer les GAT à une autre partie de ma base de code , cela ne génère qu'une seule erreur (pour l'instant), mais celle-ci ne peut pas être simplement corrigée car elle dépend d'un correctif pour mon problème susmentionné.

La limite de durée de vie peut être ajoutée au type associé (en utilisant Self: 'ast sur la version du trait). Ce test montre à quoi cela devrait ressembler :
https://github.com/rust-lang/rust/blob/db4826dd6ca48663a0b4c5ab0681258999017c7d/src/test/ui/generic-associated-types/iterable.rs#L6 -L21

bon ça ne marche que partiellement...


messages d'erreur

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

Il semble que le code passe une étape (auparavant, il se trompait avec des erreurs similaires, mais entre eux, une autre catégorie d'erreurs est apparue qui ne consistait qu'en E0107 s) hm.

edit : j'ai raté la première instruction ( where Self: 'ast ). Cette partie est maintenant corrigée. J'essaie de continuer.


contexte supplémentaire

ok, l'extrait suivant:

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)
    }
}

erreurs avec :

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) -> _`

et je sais que <F as view::ViewFn<T>>::Output<'x> ==> &'ast O et &'x O ne sont pas égaux, mais je ne sais pas comment y remédier (le compilateur n'accepte pas la limite F: for<'x> crate::view::ViewFn<T, Output = &'x O> ).
Les autres erreurs d'essai avec:

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>,
   |                                       ^^^^^^^^^^^^^^

Je ne sais pas comment exprimer un forall<'x> lié dans une affectation de type de sortie de trait, par exemple

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

n'analyse même pas ("attendu l'un des + , , , :: ou > , trouvé = ").

67510 pistes pouvant écrire cette borne. Cet exemple peut également nécessiter une normalisation paresseuse (#60471).

Le type de sortie de trait de self.clone().map est-il vraiment le type de O::as_unindented , c'est-à-dire le type d'argument de map
Je ne sais pas ce qu'est crate::View , mais la fonction map peut attendre un argument différent, d'où l'incompatibilité de type.

@sighoya J'ai lié le référentiel dans les articles précédents, voici un lien direct vers l'implémentation de crate::view::ViewFn::map

Qu'est-ce que le compilateur a dit à ce sujet ? :

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

Il n'analyse pas (encore).

@matthewjasper ,

De Rust Nomicon , les analyses suivantes :

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

Est-ce à cause de l'utilisation de Output<'x> = &'x 0> qu'il n'analyse pas ?

Oui, Output<'x>=... n'analyse pas dans cette position.

J'ai un create, grdf qui ne compile plus avec rustc 1.46.0-nightly . Quelque chose a-t-il changé autour de GAT récemment ?

Mon cas est un peu bizarre car j'ai dû utiliser une astuce pour le faire fonctionner en premier lieu. J'ai essentiellement un trait Graph avec les types d'itérateurs associés. Pour que cela fonctionne, j'ai placé tous les itérateurs dans un autre trait, 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>;

    ...
}

J'ai une implémentation de Graph qui fonctionnait très bien jusqu'à présent :

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;

    ...
}

Maintenant que j'ai mis à jour rustc, il échoue avec ce qui suit :

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

Cela n'a pas beaucoup de sens pour moi puisque T est déjà lié par 'a dans Iter ...

Okey, je l'ai fait fonctionner à nouveau en ajoutant des bornes where .
Dans le trait :

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

et dans la mise en œuvre :

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

Mais je ne suis pas sûr de comprendre la sémantique exacte des bornes where , en particulier dans l'implémentation (première fois que je vois ça). Et aussi pourquoi cela fonctionnait auparavant.

EDIT : j'ai même pu supprimer mon méchant hack et mettre les itérateurs associés dans le trait lui-même.

Ajout d'autres problèmes de blocage soulevés par @DutchGhost

Vous pouvez également ajouter https://github.com/rust-lang/rust/issues/74684 :)

Je n'aime pas écrire s'il vous plaît dépêchez-vous déjà les messages, mais cette fonctionnalité est déjà bloquée depuis près de trois ans, et je pense qu'elle est l'une des nouvelles fonctionnalités les plus souhaitées.

Où en est-on avec ça ? Le résumé de statut le plus récent ici est par @LukasKalbertodt de juin 2018 . Est-ce en attente de "chalkification" ?

Observation naïve : presque toutes mes utilisations souhaitées pour les GAT ne nécessitent qu'un seul paramètre de durée de vie. Une version réduite à vie des GAT serait-elle plus simple à fournir ?

https://github.com/rust-lang/rust/issues/44265#issuecomment -568247656 est une mise à jour (quelque peu concise).

67510 est la dernière fonctionnalité majeure ICE/manquante qui doit être implémentée.

Cette RFC rendrait-elle Monad et Functor directement possibles ? Ou y a-t-il plus de travail à faire sur les HKT ?

Est-ce que cette RFC rendrait Monad et Functor directement possibles ? Ou y a-t-il plus de travail à faire sur les HKT ?

@ibraheemdev La RFC stipule

Cela n'ajoute pas toutes les fonctionnalités que les gens souhaitent lorsqu'ils parlent de types de type supérieur. Par exemple, il n'active pas les traits comme Monad. Certaines personnes peuvent préférer implémenter toutes ces fonctionnalités en même temps. Cependant, cette fonctionnalité est compatible avec d'autres types de polymorphisme de type supérieur et n'empêche en aucune façon de les implémenter. En fait, il ouvre la voie en résolvant certains détails d'implémentation qui auront également un impact sur d'autres types de bienveillance supérieure, comme l'application partielle.

@ibraheemdev : mon sentiment est que la manière la plus idiomatique de rendre les monades et les foncteurs possibles serait d'introduire des traits associés génériques , mais les types associés génériques sont certainement une condition préalable.

Existe-t-il un chemin vers Monad qui ne nécessite pas de GAT ?

@ibraheemdev Hypothétiquement oui, il serait possible d'implémenter directement les HKT, puis d'implémenter un trait Monad par-dessus. Cependant, aucun travail n'est en cours dans cette direction, et ne le sera probablement jamais, car cette approche ne résout pas vraiment les problèmes de Rust : https://twitter.com/withoutboats/status/1027702531361857536

Peut-être que je me trompe, mais je pense que GAT autorise quelque chose comme

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

Existe-t-il un chemin vers Monad qui ne nécessite pas de GAT ?

Oui, voir Méthode d'émulation de types de type supérieur dans Rust . Cela fonctionne même sur stable maintenant.

Cette page vous a été utile?
0 / 5 - 0 notes