Rust: Problème de suivi des génériques const (RFC 2000)

Créé le 15 sept. 2017  ·  202Commentaires  ·  Source: rust-lang/rust

Problème de suivi pour rust-lang/rfcs#2000

Mises à jour:

Si vous voulez aider, jetez un œil aux problèmes de génériques de const ouverts et n'hésitez pas à envoyer un ping à @eddyb , @yodaldevoid , @oli-obk ou @lcnr pour obtenir de l'aide pour démarrer !


Stabilisation bloquante :

  • [ ] Concevoir:

    • [ ] Résolution de l'ordre des paramètres const et type, avec les paramètres par défaut

    • [ ] Décidez quel est le meilleur équilibre entre les coûts d'expérience utilisateur et de mise en œuvre pour unifier les expressions const abstraites.

    • [ ] Comment nous déterminons la bonne forme des expressions const.

  • [x] Mise en œuvre
  • [ ] Documentation

    • [ ] guide de la rouille


Problèmes de mise en œuvre restants :

  • [ ] Résoudre divers commentaires FIXME(const_generics) .
  • [ ] Résoudre les problèmes de canonisation / normalisation paresseuse.
  • [ ] Étudiez la gestion des paramètres const dans les modèles.
  • [ ] Ajouter plus de tests.
  • [ ] Implémenter les valeurs par défaut pour les paramètres const ( FIXME(const_generics:defaults) ).
  • [ ] Correction d'autres problèmes liés à A-const-generics .
  • [ ] Auditer les utilisations de has_infer_types .
  • [x] Interdire les expressions complexes pour les arguments const impliquant des paramètres (pour l'instant), par exemple {X * 2} .
  • [ ] Diagnostics d'audit (par exemple https://github.com/rust-lang/rust/pull/76401#discussion_r484819320).
A-const-fn A-const-generics A-typesystem B-RFC-approved C-tracking-issue F-const_generics T-compiler T-lang requires-nightly

Commentaire le plus utile

Voici un résumé des progrès réalisés jusqu'à présent sur les génériques const.


Avant que le travail sur les génériques de const puisse commencer correctement, une refactorisation devait être effectuée. @jplatte a assumé la tâche avec https://github.com/rust-lang/rust/pull/45930. @jplatte a ensuite commencé à travailler sur l'implémentation principale des génériques const, mais a constaté qu'ils n'avaient pas assez de temps pour continuer.

@yodaldevoid et moi avons repris là où @jplatte s'était arrêté, mais nous https://github.com/rust-lang/rust/pull/48149 , https://github.com/rust-lang/rust/pull /48452 , https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/51880.

Cela fait, la mise en œuvre des génériques const pourrait commencer sérieusement. Depuis lors, @yodaldevoid et moi avons lentement mais sûrement ajouté la prise en charge des génériques const : https://github.com/rust-lang/rust/pull/58191 , https://github.com/rust-lang/rust /pull/58503 , https://github.com/rust-lang/rust/pull/58581 , https://github.com/rust-lang/rust/pull/58583 , https://github.com/rust -lang/rust/pull/59170 , https://github.com/rust-lang/rust/pull/59355 , https://github.com/rust-lang/rust/pull/59415 , https://github .com/rust-lang/rust/pull/60058 , https://github.com/rust-lang/rust/pull/60280 , https://github.com/rust-lang/rust/pull/60284 et la plupart récemment https://github.com/rust-lang/rust/pull/59008. (Ceux-ci ont pour la plupart été séparés de la principale demande d'extraction de génériques const .)


Quel est le statut maintenant ? Certains tests génériques const fonctionnent maintenant 🎉Cependant, il y a toujours ceux qui ne fonctionnent pas, et il y a un certain nombre de FIXME dans le code que nous devons encore traiter. Nous nous rapprochons maintenant, cependant, et nous sommes au stade où il y a des fruits à portée de main si vous voulez aider.

  • Premièrement, nous avons besoin de plus de tests. Il n'y a qu'une poignée de tests génériques const jusqu'à présent, mais nous en aimerions beaucoup plus. Pour le moment, parce que nous sommes conscients d'un certain nombre de problèmes, nous n'avons pas besoin de rapports de bogues, mais nous avons besoin de réussir les tests. Si vous proposez un cas de test qui fonctionne (et ne ressemble pas trop à un test existant), n'hésitez pas à ouvrir une pull request pour l'ajouter.
  • Deuxièmement, comme mentionné, il y a un certain nombre de FIXME(const_generics) dispersés dans le code. Nous prévoyons de les parcourir, mais @yodaldevoid et moi n'avons que peu de temps, donc si vous pensez pouvoir en aborder un, allez-y (bien que vous aimeriez peut-être laisser un commentaire en disant autant, alors nous ne ' t dupliquer les efforts).

J'ai écrit un aperçu de certains des problèmes de mise en œuvre restants avant que les génériques const ne soient prêts pour des tests appropriés, dans le post du haut, pour donner une idée approximative de ce qu'il reste à faire.

Cela a pris du temps, mais nous progressons régulièrement et espérons garder le rythme. C'est motivant de voir enfin les choses commencer à se mettre en place !

Tous les 202 commentaires

44275 a ajouté un prédicat ConstEvaluatable , et WF([T; expr]) requiert maintenant ConstEvaluatable(expr) , car expr est évalué paresseusement (même s'il ne s'agit que d'un entier littéral). Remplir ce prédicat nécessite que l'expression soit évaluée avec succès, tandis que la normalisation ignore l'erreur et laisse simplement l'expression Unevaluated qu'elle a trouvée, intacte, ce qui est plus ou moins ce qui se passe avec les projections de type associées. Je m'attends à ce que le même système évolue pour const les génériques.

@EpicatSupercell a exprimé son intérêt à travailler là-dessus, je les encadrerai tout au long de la mise en œuvre initiale. Cependant, nous ne pouvons pas aller trop loin en raison des limitations décrites dans #44275.

C'est-à-dire que nous avons besoin de la normalisation paresseuse de @nikomatsakis pour permettre aux expressions constantes intégrées dans les types d'observer les limites de la portée (à partir de l'élément fonction / définition de type / impl / etc. dans lequel elles se trouvent), sans produire de dépendances cycliques la moitié du temps.

Waypoints de mise en œuvre (pour un mentorat plus direct, recherchez @eddyb sur Gitter ou eddyb sur IRC) :

  • Déclaration / Syntaxe :
  • Déclaration / Sémantique

    • Structures de données : ty::Generics - ajoutez des paramètres const côté de ceux de type

    • Conversion de HIR : generics_of - créer ty::ConstParameterDef partir de hir::ConstParam

  • Utilisation / Résolution de nom

    • Structures de données : Def - ajouter une variante pour les paramètres const

    • Passe de résolution de nom : with_type_parameter_rib - prend en charge à la fois le type et les génériques const

  • Utilisation / Sémantique
  • Inférence

Notez que tout cela devrait autoriser impl<T, const N: usize> Trait for [T; N] {...} , mais pas réellement passer une expression constante à un type/une fonction, par exemple ArrayVec<T, 3> .

J'aimerais tenter le coup :)

@jplatte @eddyb Des nouvelles à ce sujet ?

@samsartor il y avait une refactorisation qui devait être faite avant le travail d'implémentation principal. C'est maintenant presque fait (j'attends des retours actuellement). Je ne sais pas vraiment combien de travail il y a après ça. J'ai commencé par analyser const params, avant la refactorisation. Ce n'est pas fait, mais je devrais pouvoir le faire assez rapidement.

@jplatte Ce serait bien si vous pouviez lier la demande de tirage. Je n'ai pas pu le trouver.

Il n'y a pas encore de RP. Tout mon travail peut être trouvé ici .

Beau travail jusqu'à présent @jplatte 🍻

Il y a maintenant un PR pour le travail de base ( Generics refactoring): #45930

Je pense que cela est déjà inclus, mais il serait bon de gérer le pliage de style C++ pour gérer des tableaux à n dimensions de style algèbre linéaire avec des longueurs variables, c'est-à-dire un 4x4 [[f64;4];4]ou un tableau de dimensions 4x3x5x6 [[[[ f64;6];5];3];4] et être capable d'envelopper et de générer correctement des méthodes spécialisées à la fois pour cela ET des implémentations de traits appropriées pour des vecteurs scalaires correctement dimensionnés, etc. Voir https://gist.github.com/huhlig /8b21850b54a75254be4b093551f8c2cb pour un exemple rudimentaire.

Je ne me souviens pas que quelqu'un ait proposé des expressions de repli pour Rust auparavant, encore moins dans le cadre de cette RFC. Mais puisqu'il s'agit de Rust, y a-t-il une raison pour laquelle nous ne pourrions pas implémenter les expressions de repli comme une macro ordinaire, plutôt que comme une nouvelle syntaxe dédiée ?

Quelles sont les prochaines étapes maintenant que #45930 est fusionné ? @jplatte

@kjaleshire La prochaine étape consiste à implémenter l'analyse des génériques const (voir le commentaire de @eddyb plus haut). J'ai déjà commencé à travailler là-dessus avant qu'il ne devienne clair que le refactoring dans le refactoring serait nécessaire. Mon travail existant sur cela peut être trouvé ici ; il n'a pas encore été rebasé depuis que le PR de refactoring a été fusionné.

@jplatte Je crois que vous vouliez mentionner @kjetilkjeka.

Merci pour la mise à jour! Je suis sûr que je suis loin d'être le seul à attendre cette fonctionnalité avec impatience. Continuez votre bon travail!

@jplatte ne voulant pas être impatient, mais y a-t-il eu du travail là-dessus? As-tu besoin d'aide? Quelqu'un devrait-il aider?

@est31 Désolé. Je n'ai pas eu le temps de travailler dessus depuis un moment, et je ne sais pas vraiment quand j'aurai le temps. Peut-être vaut-il mieux que quelqu'un d'autre continue là où je m'étais arrêté. Je pense que la partie analyse est principalement terminée. Il y a deux endroits dans le code où je ne savais pas quoi faire dans le cas d'un paramètre générique étant un paramètre const :

Il n'y a pas encore non plus de tests pour l'analyse des génériques const (et pour le joli code d'impression que j'ai mis à jour en même temps). Je ne sais pas quelles autres informations je peux fournir qui seraient nécessaires / utiles pour que quelqu'un d'autre continue à travailler dessus, mais n'hésitez pas à me contacter si quelque chose n'est pas clair à propos de mon code.

@jplatte ~ None dans le premier et rien dans le second~~ (cc @pnkfelix @nikomatsakis)

EDIT : rien à faire dans les deux cas.

@eddyb Pour le premier extrait lié, êtes-vous sûr que None doit être renvoyé ? Je ne comprends peut-être pas ce qui se passe ici, mais il me semble qu'il ne faut rien faire. Si None est renvoyé, d'autres paramètres qui pourraient être dangereux seront ignorés.

@yodaldevoid Désolé, vous avez raison, je n'avais pas réalisé que c'était le Generics .

Je voudrais reprendre là où @jplatte s'est arrêté. Je travaille là- dessus depuis quelques jours maintenant, en @eddyb car j'ai eu le temps juste de voir si je pouvais aller quelque part. Je suis descendu à la section "Utilisation / Sémantique" à ce stade. Je passerai probablement assez tôt sur l'un des chats pour poser des questions.

@yodaldevoid Super à entendre. Je ne sais pas pour le Gitter, mais vous obtiendrez certainement de nombreux conseils sur #rustc et/ou #rust-internals sur IRC !

@yodaldevoid : seriez-vous fait jusqu'à présent ici .) Peut-être pourrions-nous discuter sur IRC (#rustc ou #rust-internals) à ce sujet ?

@varkor Il semble que vous soyez plus avancé que moi. Je serais certainement prêt à collaborer. J'essaierai de vous attraper sur IRC à un moment donné et en attendant voir si j'ai fait quelque chose que vous n'avez pas déjà fait.

Y a-t-il des progrès dans ce domaine ?
J'écris du code pour l'embarqué et j'ai vraiment besoin de génériques const.

@qwerty19106
Des progrès sont réalisés sur ce point, quoique lentement. @varkor et moi avons travaillé dessus par

Au-delà de la simple implémentation des génériques const, nous (varkor) avons fait un peu de nettoyage pour rendre tout cela possible (voir #48149 et #48523). Je pense que le plan actuel est d'attendre que ces deux demandes d'extraction soient traitées avant de tirer des génériques const, mais varkor peut en parler davantage.

Je comprends vraiment votre besoin de génériques const pour le travail embarqué. J'ai commencé là-dessus parce que moi aussi je veux vraiment des génériques const afin que je puisse nettoyer et sécuriser le type de larges pans de code intégré.

TL;DR : Les progrès avancent, mais c'est complexe. Je te sens sur le front incrusté.

Pour la case à cocher "documentation", ce serait bien d'obtenir quelque chose dans le guide rustc .

Merci @yodaldevoid pour votre réponse. J'attends avec impatience la fin de votre travail.

Mise à jour pour les curieux (puisque j'étais aussi curieux). Re : les deux PR mentionnés ci -

@mark-im Bonnes choses ! Beau travail de @varkor. Quand est l'ETA à peu près, tu sais ? :-)

Salut, @ flip111.

On dirait que le deuxième grand refactoring PR #48149 a été fusionné :)

/cc moi

Suivant @varkor PR #51880

Je voulais juste faire une mise à jour rapide, car je sais que certaines personnes ont posé des questions sur les progrès des génériques const.

Pour donner un peu de contexte, en mars , @yodaldevoid et moi avions une implémentation initiale qui fonctionnait presque (le gros de l'implémentation semblait être fait, et nous étions juste en train de nettoyer quelques plantages restants). Cependant, la branche sur laquelle nous travaillions était pré-miri. Lorsque miri a été fusionné (et dans un certain nombre de PR ultérieurs), le code pour gérer l'évaluation constante a considérablement changé, ce qui signifie que beaucoup de ce que nous avions fait est devenu obsolète.

En plus de cela, il a été décidé que le code des paramètres génériques devait être nettoyé en général avant d'ajouter des génériques const, à la fois pour améliorer la lisibilité, mais aussi pour faire des erreurs où nous avons oublié de traiter les génériques const dans un certain cas plus difficile à Fabriquer. Cela a été fait dans https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/48149 et sera terminé dans https://github. com/rust-lang/rust/pull/51880. Ceux-ci étaient un peu plus impliqués que ce à quoi je m'attendais initialement, et ils ont mis un peu plus de temps à passer que prévu.

En attendant, @yodaldevoid et moi avons travaillé pour rendre notre implémentation d'origine compatible avec toutes les modifications ultérieures de rustc. Cela a pris du temps, mais nous y arrivons (même s'il y a le problème éternel de ne jamais avoir autant de temps que prévu). J'espère que nous aurons bientôt de bonnes nouvelles à ce sujet. (Pendant ce temps, https://github.com/rust-lang-nursery/chalk a fait de bons progrès, ce qui devrait résoudre certaines des difficultés décrites à l' origine par @eddyb .)


Certaines personnes ont demandé comment elles pouvaient aider : je pense qu'à ce stade, il sera plus facile pour nous d'essayer de terminer la mise en œuvre initiale, puis de voir quelles parties nécessitent une attention particulière. Une fois que cela sera prêt, nous aurons besoin de beaucoup de tests, en utilisant des génériques const à différents endroits (y compris des non valides, pour les messages d'erreur), donc c'est certainement quelque chose que nous pourrions faire avec beaucoup d'aide ! Nous vous tiendrons au courant quand cela arrivera !

Désolé si ce n'est pas l'endroit approprié pour cela, mais j'ai une suggestion concernant l'égalité des expressions const abstraites. En général, cela se réduit directement à un typage entièrement dépendant et à un territoire indécidable. Cependant, dans rust, tout est finalement instancié avec des valeurs/types concrets, nous pouvons donc affirmer certaines égalités et reporter leur vérification à des instances monomorphes.

Ce que je veux dire, c'est qu'un moyen relativement simple de vérifier l'égalité des expressions const abstraites est le suivant :

  • traite automatiquement l'égalité syntaxique (c'est-à-dire que N+1 == N+1 devrait fonctionner immédiatement)
  • permettre à l'utilisateur, au moment de la définition, d'ajouter des équations telles que N+M == M+N (peut-être dans la clause where ?). Ces équations peuvent être utilisées par le contrôle d'égalité (en utilisant une certaine forme de fermeture de congruence). Une définition qui ne vérifie pas le type à l'aide de ces équations fournies est rejetée.
  • à un point d'expansion monomorphe, toutes les équations peuvent être vérifiées en calculant réellement les exprs const, qui ne sont plus abstraites. Ici, il y a un choix de conception : si une équation qui se réduit à false , il peut y avoir une erreur de compilation ("les équations sont des axiomes") ou le trait ne peut pas être instancié ("les équations sont des contraintes")
  • à un point d'expansion paramétré, ces équations sont reportées : si vous avez une fonction f paramétrée par NN+0==N , cette équation ne doit pas être perdue dans un appelant g car il devra être vérifié à chaque endroit monomorphe où g est appelé.

Les avantages de cette méthode sont qu'il n'y a pas besoin d'un prouveur de théorème, d'un solveur SMT ou d'un réécrivain arithmétique dans Rustc lui-même. "Seulement" un contrôle d'égalité syntaxique des types, modulo des alias et modulo un ensemble d'équations fournies par l'utilisateur.
L'inconvénient est que c'est plus manuel pour l'utilisateur car des égalités apparemment évidentes comme M+N=N+M doivent être ajoutées explicitement.

Exemple:

/// this works directly because of syntactic checks
fn add_end<T:Copy, const N: usize>(a: &[T;N], b: T) -> [T;N+1] {
  let x : [T;N+1] = [b;N+1];
  x[0..N] = a;
  x
}

/// this should work already
fn append<T:Copy, const M:usize, const N:usize>(a: &[T;M], b: &[T;N])
  -> [T;{M+N}]
{ … }

/// Here the equation M+M==N must be carried over or checked whenever this function is used
fn sum_pairwise_append<const M: usize, const N: usize>(a: &[i32, M],b: &[i32;N])-> [T;N]
  where M+M == N {
  let mut res : [i32; N] = append(a,a);
  for i in 0 .. N { res[i] += b[i] };
  res
} 


fn main() {
  let a: [i32; 2] = [1;2];
  let b: [i32; 4] = [2;4];
  let _ = sum_pairwise_append(a, b);  // need to check 2+2=4 
}

Les équations comme axiomes

Cela signifie que e1 == e2 doit toujours être vrai, donc ce n'est pas vraiment une contrainte where mais plutôt une assert_eq!(e1,e2) qui peut être utilisée par le vérificateur de type. Ici, l'implémenteur promet que cela est toujours vrai et expose ses utilisateurs à des erreurs de compilation s'ils fournissent des paramètres qui réfutent l'équation.

Les équations comme contraintes

Ici, une clause where e1 == e2 est une contrainte qui doit être satisfaite pour l'utilisation réussie de ce trait/fonction paramétré. Cela signifie que e1 == e2 n'a pas à être toujours valable, ce qui peut être intéressant pour les équations vraies uniquement sur un domaine, comme (x*y) / z == (y*x) / z qui échouerait à instancier pour z==0 .

@c-cube Nous avons déjà un système pour les "clauses implicites where ", au moins pour les fonctions, dérivées des " règles WF (well-formedness)", c'est-à-dire si vous définissez un fn foo<'a, 'b>(x: &'a &'b i32) {} alors il faut que les appelants satisfassent 'b: 'a (en raison du besoin de WF(&'a &'b i32) -> &'b i32: 'a -> 'b: 'a ).

Nous pouvons réutiliser ce système (en fait, c'est déjà implémenté) pour pousser les contraintes données par la signature (de la forme "cette expression constante est évaluée avec succès") jusqu'aux appelants, alors que tout ce qui n'est utilisé qu'à l'intérieur du corps aurait toujours besoin de where clauses.

La plupart de tout ce que vous décrivez semble proche de ce qui est prévu (y compris une forme d'"égalité syntaxique"), mais nous ne voulons pas de contrôles "monomorphisation-temps", nous pouvons à la place avoir des moyens (soit implicites soit via where clauses) pour "propager les contraintes aux instanciateurs", puis nous produisons des erreurs où les contraintes sont "encore trop génériques" (donc inconnues si elles sont vérifiées), ou sinon si nous pouvons les évaluer et qu'elles ne le sont pas.

La plupart de tout ce que vous décrivez semble proche de ce qui est prévu

@eddyb connaissez-vous des articles de référence qui

@ flip111 Probablement certains des RFC. Peut-être que @withoutboats sait mieux.

# 51880 est fait : tada :: tada:

@withoutboats @oli-obk Que pensez-vous du blocage de l'unification correcte des expressions comme N + 1 (de par exemple [T; N + 1] ) pour obtenir une forme de base d'évaluation symbolique dans miri ?

Je pense que nous aurions déjà un mécanisme pour l'encoder, lorsque @varkor ajoute ConstValue::{Infer,Param} , nous pouvons l'utiliser pour suivre les valeurs "(peu superficiellement) valides mais inconnues" (symboliques), puis inclure également la valeur (principalement entiers) opérations et appels en plus de cela.

Toute décision non assert contrôle de flux nécessiterait toujours des valeurs connues, mais assert lui-même pourrait peut-être être réifié en tant que AssertThen(condition, assert message, success value) .
(Le condition et le success value pourraient être symboliques !)

Être capable d'exporter les valeurs symboliques dans ty::Const signifie que nous pouvons implémenter une partie de l'unification en dehors de miri, en traitant (la plupart ?) les opérations comme opaques.

Nous devons faire attention à ne rien supposer de variables d'inférence, mais pour par exemple N + 1 je pense que nous pouvons soutenir l'unification de deux Add(Param(N), Bits(1)) ensemble.

@varkor Un cas de test, je pense, devrait fonctionner avec une normalisation paresseuse, pas d'unification :

/*const*/ fn append<const N: usize, T>(xs: [T; N - 1], x: T) -> [T; N] {
    let new = MaybeUninit::<[T; N]>::uninitialized();
    unsafe {
        let p = new.as_mut_ptr() as *mut T;
        (p as *mut _).write(xs);
        p.add(N - 1).write(x);
    }
    new.into_inner()
}

Cela ne fonctionnerait que parce que :

  • N - 1 apparaît au niveau du type exactement une fois

    • tout peut se référer à cette expression, de manière opaque

    • (solution de contournement potentielle : type ArrayWithoutLast<T, const N: usize> = [T; N - 1]; )

  • c'est dans une position d'argument dans la signature

    • (je ne me souviens pas si la position de retour fonctionnerait également. @nikomatsakis ?)

    • les appelants peuvent prouver qu'il s'agit de WF, en fournissant des valeurs concrètes pour N

    • "bouillonner" l'exigence WF a besoin d'unification / ArrayWithoutLast

    • d'autres utilisations nécessiteraient « l'intégration dans une clause where », par exemple [T; N - 1]: Sized



      • peut même abuser de where [T; N - 1]: (est-ce que cela est ignoré aujourd'hui ? aïe !)


      • contourner à nouveau l'unification avec where ArrayWithoutLast<T, N>: ...



Donc, dans l'ensemble, nous pouvons probablement nous fier aux règles WF pour forcer la "validation" des expressions const sur les utilisateurs, mais nous voudrions toujours l'unification pour d'autres choses (y compris ne pas avoir besoin de hacks comme ArrayWithoutLast ).

53645

Petite question qui découle de la discussion sur les unités de mesure. Que faire si le type générique sur constant n'en dépend pas directement ? Par exemple:

struct Foo<T, const N: usize> {
    a: T,
    // Will such array be "the way" to handle this problem?
    // Or do we want some kind of `std:marker::PhantomConstData`? (which can be a simple
    // type alias to the same array)
    // Or maybe make `PhantomData` to accept constants?
    _phantom: [(), N],
}

_phantom : [(), N],

Je suppose que vous vouliez dire [(); N] , mais cela ne fonctionnera que pour usize .

Je pense qu'il serait logique d'avoir un marker::PhantomConst spécial permettant n'importe quel type const .

Hmm… En relisant la RFC avec les derniers commentaires en tête, je me demande : est-ce que quelque chose comme ça serait autorisé ?

struct Foo<T, const V: T> {
    _phantom: PhantomConst<T, V>,
}

Je ne vois pas qu'il soit interdit n'importe où, mais j'aurais pensé qu'il mériterait au moins un exemple.

@Ekleog : pour autant que je sache, les types de paramètres const peuvent ne pas dépendre initialement des paramètres de type (bien que cela aurait certainement du sens pour une extension). (La mise en œuvre est plus délicate si nous le permettons, il est donc logique de commencer par la version la plus simple.)

Comment sont les progrès à ce sujet? Connaissons-nous une heure approximative à laquelle cela devrait frapper la nuit ?

@Zauberklavier Dès que cette pull request est faite. Pour en citer :

Il y a un long chemin à parcourir

@newpavlov J'ai supposé que les paramètres constants seraient autorisés sans être utilisés dans les champs.
La raison pour laquelle les paramètres de type doivent être utilisés est la variance, mais les constantes n'ont pas ce problème.

@eddyb c'est aussi ce dont je me souviens de la discussion RFC.

@varkor Cela signifie donc que PhantomConst proposé par @cuviper ne peut pas exister dans l'état actuel de cette RFC… non ? bien que les derniers commentaires semblent indiquer qu'il n'y a pas besoin de PhantomConst toute façon.

Les paramètres const ont-ils un impact sur la variance des paramètres de type qu'ils impliquent ?

Actuellement, je pense que les génériques const ne peuvent pas avoir de paramètres de type dans leur type.

Je ne suis pas clair sur ce que la première version de travail sera capable de faire.

Par exemple si le code dans ce commentaire compilerait :
https://github.com/rust-lang/rfcs/pull/2581#discussion_r230043717

Si ce code était compilé, ce serait un moyen d'imposer des contraintes pour les constantes avant d'obtenir une syntaxe pour cela.

@rodrimati1992 Vous pouvez probablement simplement faire (): IsTrue<{N < 128}> sans type séparé.
Je suppose que vous voulez réutiliser la contrainte et la faire fonctionner ? (car copier l'expression N < 128 ne le fera pas fonctionner, au départ)
Vous pourriez utiliser trait Lt128<const N: usize> = IsTrue<{N < 128}>; , je suppose.
Il y a aussi la possibilité d'écrire simplement where [(); 128 - N], , je pense (mais je ne suis pas sûr que nous garantissions que cela va paniquer).

@rodrimati1992 Vous pouvez probablement simplement faire (): IsTrue<{N < 128}> sans type séparé.
Je suppose que vous voulez réutiliser la contrainte et la faire fonctionner ? (car copier l'expression N < 128 ne le fera pas fonctionner, au départ)
Vous pourriez utiliser trait Lt128<const N: usize> = IsTrue<{N < 128}>; , je suppose.
Il y a aussi la possibilité d'écrire simplement where [(); 128 - N], , je pense (mais je ne suis pas sûr que nous garantissions que cela va paniquer).

Donc, avec les alias de traits que je pourrais réécrire, c'est comme ça ? :

trait AssertLessThan128<const N:usize>=
    Assert<{N<=128}, (
        Str<"uint cannot be constructed with a size larger than 128,the passed size is:",
        Usize<N>>
    ) >;

L'idée avec l'Asserttrait utilise une erreur de type pour imprimer un message d'erreur intégré dans un type, qui dans le cas de AssertLessThan128 est :

(Str<"uint cannot be constructed with a size larger than 128,the passed size is:",Usize<N>>)

ce que je pense être plus utile que where [(); 128 - N], , car il vous indique dans le message d'erreur pourquoi l'erreur de compilation s'est produite.

Vous pouvez aussi faire if !(N < 128) { panic!("...") } dans une constante, je pense ?

Je pensais que l'architecture std::fmt ne serait pas là avant l'arrivée des contraintes const trait (ou quelque chose de similaire) ?

Avec cette chose, vous pouvez avoir des messages d'erreur avec plusieurs valeurs (intégrés dans les types).

Peut-être vaut-il mieux attendre que les génériques const en parlent, car il sera plus facile de montrer des exemples exécutables.

@rodrimati1992 voir https://github.com/rust-lang/rfcs/blob/master/text/2345-const-panic.md , il va y avoir un cas particulier pour obtenir const panic!() avant qu'il ne le fasse être possible via d'autres fonctionnalités (au début, cela ne permettra probablement pas le formatage personnalisé des valeurs).

@rodrimati1992 Ahh, je vois, c'est en effet une astuce astucieuse !

Quel est le statut de ceci?

La prochaine étape est toujours https://github.com/rust-lang/rust/pull/53645 AFAIK.

C'est correct. Pour donner une mise à jour rapide pour ceux qui ne suivent pas le PR #53645, les génériques const ont compilé et fonctionnent dans au moins un cas d'utilisation simple. Il ne reste plus qu'à terminer le codegen pour d'autres cas d'utilisation, y compris les génériques const dans arrys, et un certain nettoyage de sortie d'erreur. Après cela, le PR devrait être prêt à fusionner et les gens peuvent commencer à jouer avec.

Peut-être hors sujet, mais cela permettra-t-il à une variante ou à un fork de Chunks et des méthodes associées d'avoir Item un tableau de taille fixe au moment de la compilation au lieu d'une tranche ? Cela permettrait à une boucle for de se déstructurer/se lier à un modèle irréfutable qui mappe les éléments du bloc à des variables, telles que for (first, second) in arr.chunks(2) qui échoue actuellement, et je suppose (sans aucune justification certes) qu'elle permettrait plus d'optimisation dans certains cas d'utilisation.

@jeffvandyke Vous ChunksExact en particulier. Un tel changement casserait l'API, il ne peut donc pas être fait. Nous pourrions ajouter de nouvelles API qui donneraient des références de tableau à l'avenir, mais nous ne pouvons pas casser celles existantes.

@jeffvandyke Vous ChunksExact en particulier. Un tel changement casserait l'API, il ne peut donc pas être fait. Nous pourrions ajouter de nouvelles API qui donneraient des références de tableau à l'avenir, mais nous ne pouvons pas casser celles existantes.

J'attends seulement que cela soit implémenté et stabilisé avant de proposer un PR qui ajoute une telle API en plus de ChunksExact et ses variantes :)

Les variantes d'exécution et de compilation ont toutes deux leurs cas d'utilisation, vous ne connaissez pas toujours la taille de votre morceau à l'avance. Du point de vue de l'optimisation, si vous utilisez ChunksExact avec une constante, cela devrait être plus ou moins le même selon mes tests. Le compilateur peut optimiser toutes les vérifications de limites.

à mettre en œuvre et à stabiliser

Je suggérerais de ne pas attendre la stabilisation car une telle API serait une autre bonne utilisation pour aider à exercer cette fonctionnalité pour la stabilisation.

Je suppose que cela ne fonctionne pas encore pour les blocs impl ? J'ai essayé

#![feature(const_generics)]

struct The<const Val: u64>();

impl<const Val: u64> The<Val> {
    fn the() {
        println!("{}", Val);
    }
}

mais, comme cela a été effectivement prévenu, le compilateur a planté avec

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
   1: std::sys_common::backtrace::_print
   2: std::panicking::default_hook::{{closure}}
   3: std::panicking::default_hook
   4: rustc::util::common::panic_hook
   5: std::panicking::rust_panic_with_hook
   6: std::panicking::continue_panic_fmt
   7: rust_begin_unwind
   8: core::panicking::panic_fmt
   9: core::slice::slice_index_order_fail
  10: rustc_resolve::Resolver::resolve_ident_in_lexical_scope
  11: rustc_resolve::Resolver::resolve_path
  12: rustc_resolve::Resolver::resolve_path_without_parent_scope
  13: rustc_resolve::Resolver::smart_resolve_path_fragment
  14: rustc_resolve::Resolver::smart_resolve_path
  15: <rustc_resolve::Resolver<'a> as syntax::visit::Visitor<'tcx>>::visit_ty
  16: syntax::visit::walk_generic_args
  17: syntax::visit::walk_ty
  18: rustc_resolve::Resolver::with_generic_param_rib
  19: rustc_resolve::Resolver::resolve_item
  20: rustc_resolve::Resolver::resolve_crate
  21: rustc::util::common::time
  22: rustc_interface::passes::configure_and_expand_inner
  23: rustc_interface::passes::configure_and_expand::{{closure}}
  24: <rustc_data_structures::box_region::PinnedGenerator<I, A, R>>::new
  25: rustc_interface::passes::configure_and_expand
  26: <rustc_interface::queries::Query<T>>::compute
  27: <rustc_interface::queries::Query<T>>::compute
  28: <rustc_interface::queries::Query<T>>::compute
  29: rustc_interface::queries::<impl rustc_interface::interface::Compiler>::prepare_outputs
  30: rustc_interface::interface::run_compiler_in_existing_thread_pool
  31: <std::thread::local::LocalKey<T>>::with
  32: <scoped_tls::ScopedKey<T>>::set
  33: syntax::with_globals

L'erreur est liée au const dans impl<const Val: u64> car la suppression de cette partie provoque d'autres erreurs mais ne plante pas.

mais des accessoires à Rust pour même considérer cette fonctionnalité. Je ne savais pas si ça marcherait mais la syntaxe me paraissait naturelle, j'y suis allé, et lo rustc a dit que ça existait :)

Je ne suis pas surpris que cela ne fonctionne pas, car cette fonctionnalité très attendue est toujours en cours d'installation via le compilateur au moment où nous parlons (voir par exemple #59008 et #58581 et les travaux antérieurs sur #53645 qui ont été abandonnés car le PR était trop gros , mais toujours ouvert en tant que tracker pour annoncer les progrès).

Cependant, je ne suis pas sûr qu'il faille s'attendre à des accès aux tranches hors limites des stubs d'implémentation actuels. @varkor @yodaldevoid , pouvez-vous jeter un œil ?

Oui, l'avertissement est correct : les génériques const ne sont pas encore fonctionnels sous quelque forme que ce soit. Il reste encore quelques demandes de tirage avant qu'elles ne soient prêtes à commencer à jouer avec.

Désolé si ce n'est pas le bon endroit pour poser des questions mais je n'ai pas pu trouver mieux. Juste 2 questions :

  1. Une seule fonction peut-elle être conditionnellement const? Par exemple, une fonction pourrait avoir 2 signatures comme :

    const fn foo<A: const T>(x: T)  // `A` const implements `T`
    fn foo<A: T>(x: A)              // `A` implements `T` normally
    

    Dans ce cas, foo est const ssi A: const T ; si A n'implémente pas T , il satisfait toujours la limite mais foo n'est plus const. Il est également important pour l'auteur de pouvoir spécifier n'importe quelle borne générique pour des exemples plus complexes (ex. where A::Output : Bar ). Un bon exemple de ceci est même l'arithmétique simple :

    // This only accepts types that const implement `T`
    const fn square_const<T: const Mul>(x: T) -> T {
      x*x
    }
    
    // This accepts types that implement `T` any way, but it is not const
    // This function behaves identically to `square_const`
    // But has a different signature and needs a different name
    fn square<T: Mul>(x: T) -> T {
      square_const(x)
    }
    
    let a: u8 = 5;
    let b: FooNumber = FooNumber::new();
    square_const(a); // `u8` const implements Mul
    square(b); // `FooNumber` implements `Mul` normally, so we need a separate function?
    

    Je suis convaincu qu'il devrait certainement y avoir un moyen de le faire, et je suis surpris qu'il ne soit pas mentionné dans le RFC (à moins que je ne l'aie raté ?).

  2. _[moins important] :_ Y aura-t-il un moyen de détecter dans le corps d'une fonction const si nous sommes en cours d'exécution à la compilation ou à l'exécution ? Je pense qu'une macro similaire à cfg!() serait une extension utile, ne serait-ce que pour le débogage. cfg!() est déjà évalué au moment de la compilation, donc je pense (/deviner) qu'il devrait être capable de savoir si la fonction est utilisée comme const ou comme fonction régulière puisque cela aussi est déterminé à la compilation -temps. Cependant, c'est moins important que ma 1ère question.

@Coder-256

  1. Oui, voir https://github.com/rust-lang/rfcs/pull/2632.
  2. Je ne sais pas si cela devrait même être possible, bien que compte tenu de ce qui précède, je ne suis pas sûr que ce soit nécessaire non plus.

@Coder-256 Ces deux questions ne sont pas liées aux génériques const, mais plutôt aux fonctions const. Les génériques de const sont destinés à être génériques sur les consts (par exemple, foo<2>() ) plutôt que les fonctions pouvant être exécutées au moment de la compilation. J'imagine que c'est pourquoi vous n'avez pas trouvé les réponses à vos questions dans la RFC 2000.

@rpjohnst Merci, mais je pense que je n'ai peut-être pas été clair. J'ai déjà vu à la fois rouille-lang/rfcs#2632 et rouille-lang/rfcs#2000, mais je suis presque sûr que ce n'est pas mentionné dans l'un ou l'autre. (mais je peux me tromper ?) Ce que je demande, ce sont les fonctions const conditionnellement . Voir les exemples que j'ai écrits car c'est difficile à décrire.

@yodaldevoid Oups tu as raison, où dois-je demander ça ?

Quant à la question macro, je suis d'accord qu'elle n'a pas vraiment d'utilité maintenant que j'y pense

Ce que je demande, ce sont les fonctions const conditionnellement. Voir les exemples que j'ai écrits car c'est difficile à décrire.

Votre définition square_const peut être utilisée à la place de square (c'est-à-dire qu'elle est forcée à une fonction avec la signature équivalente au moment de l'exécution). Voir https://github.com/rust-lang/rfcs/pull/2632 pour une discussion sur ce comportement (en outre, ce fil est le bon endroit pour poser des questions sur les interactions entre const fn et les limites de trait).

@varkor Je ne suis pas convaincu que ce soit le cas (puisque les limites des traits changent), mais je demanderai dans rust-lang/rfcs#2632.

Rapport d'incident :

Code:

#![feature(const_generics)]

use std::marker::PhantomData;

struct BoundedU32<const LOWER: u32, const UPPER: u32> {
    value: u32,
    _marker: PhantomData<(LOWER, UPPER)>,
}

Compilateur:

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.35.0-nightly (719b0d984 2019-03-13) running on x86_64-unknown-linux-gnu

note: compiler flags: -C codegen-units=1 -C debuginfo=2 --crate-type lib

note: some of the compiler flags provided by cargo are hidden

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

@Jezza : les génériques const ne sont pas encore entièrement implémentés et ne devraient pas fonctionner. Nous ferons une annonce lorsqu'il sera temps de commencer à expérimenter avec #![feature(const_generics)] (dont vous serez averti si vous êtes abonné à ce numéro).

@varkor Attendez , l'exemple de _marker: PhantomData<(LOWER, UPPER)> - ce sont deux paramètres const utilisés comme types, pourquoi rustc_resolve n'a-t-il pas produit d'erreur ?

Bon point : je vais enquêter là-dessus. Notez que ce n'est qu'un problème avec #![feature(const_generics)] , donc ce n'est pas un problème critique (l'utilisation de consts non génériques produit l'erreur comme prévu). Peut-être qu'il n'atteint jamais rustc_resolve .

~ @eddyb : cet ICE vient de resolve_ident_in_lexical_scope , donc j'imagine que c'est probablement lié à https://github.com/rust-lang/rust/issues/58307.~

Edit : en fait, peut-être pas — cela semble s'appliquer uniquement à macro_rules! .

Minimisé :

#![feature(const_generics)]

struct S<const C: u8>(C);

Résolvez les ICE avant de produire l'erreur « type attendu, valeur trouvée ».

L'index est hors limites ici :
https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/lib.rs#L3919

La nuit actuelle produit "le paramètre N n'est jamais utilisé" pour le code suivant :

struct Foo<const N: usize> { }

D'après la discussion précédente

@newpavlov les paramètres de type normal doivent être utilisés, faut-il pouvoir faire PhantomData<n> ?

Je suppose qu'il est déjà possible de faire PhantomData<[(); N]> . Pas sûr que ce soit quelque chose que nous voulons réellement appliquer, cependant, comme AFAIU, le but de PhantomData est de marquer la variance, et il n'y a AFAIU aucune notion de variance en ce qui concerne. un paramètre générique const.

Et cela ne fonctionne que lorsque N est de type usize .

Nous avons décidé qu'il n'était pas nécessaire d'utiliser les paramètres const lors de la discussion RFC et l'implémentation actuelle est un bogue.

@withoutboats Pouvez-vous indiquer où dans la RFC cela est indiqué? J'ai essayé de trouver quelque chose à cet effet et j'ai dû le rater.

je ne sais pas si cela a été inclus dans le texte RFC

@withoutboats Pourriez-vous indiquer où cela a été discuté ? J'ai parcouru le RFC PR, mais cela ne m'a pas sauté aux yeux.

@yodaldevoid Ce commentaire a été mentionné précédemment : https://github.com/rust-lang/rust/issues/44580#issuecomment -419576947 . Mais ce n'était pas dans le RFC PR, plutôt sur cette question.

Je ne peux pas parcourir l'historique des commentaires d'il y a plusieurs années maintenant, mais je peux expliquer : l'utilisation de variables de type est requise comme bloqueur pour vous faire réfléchir à la variance de ces paramètres (IMO, cela est également inutile et nous pourrions par défaut utiliser covariant, mais c'est un problème séparé). Les paramètres de const n'ont aucune interaction avec la variance, donc cela n'aurait aucune motivation.

@HadrienG2 Merci d'avoir trouvé un commentaire pertinent.

@withoutboats Je ne m'attendais pas vraiment à ce que vous parcouriez toutes les années de commentaires pour cela, c'était seulement un espoir que vous l'ayez déjà en main.

Merci pour l'explication. Je dois admettre que je ne peux jamais penser à la variance, peu importe le nombre de fois que j'essaie de l'apprendre, mais même sans cela, une fois repensé, il est logique de ne pas nécessiter l'utilisation de paramètres de coût. Je vais jeter la correction de ce bogue sur notre liste de FIXME.

Voici un résumé des progrès réalisés jusqu'à présent sur les génériques const.


Avant que le travail sur les génériques de const puisse commencer correctement, une refactorisation devait être effectuée. @jplatte a assumé la tâche avec https://github.com/rust-lang/rust/pull/45930. @jplatte a ensuite commencé à travailler sur l'implémentation principale des génériques const, mais a constaté qu'ils n'avaient pas assez de temps pour continuer.

@yodaldevoid et moi avons repris là où @jplatte s'était arrêté, mais nous https://github.com/rust-lang/rust/pull/48149 , https://github.com/rust-lang/rust/pull /48452 , https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/51880.

Cela fait, la mise en œuvre des génériques const pourrait commencer sérieusement. Depuis lors, @yodaldevoid et moi avons lentement mais sûrement ajouté la prise en charge des génériques const : https://github.com/rust-lang/rust/pull/58191 , https://github.com/rust-lang/rust /pull/58503 , https://github.com/rust-lang/rust/pull/58581 , https://github.com/rust-lang/rust/pull/58583 , https://github.com/rust -lang/rust/pull/59170 , https://github.com/rust-lang/rust/pull/59355 , https://github.com/rust-lang/rust/pull/59415 , https://github .com/rust-lang/rust/pull/60058 , https://github.com/rust-lang/rust/pull/60280 , https://github.com/rust-lang/rust/pull/60284 et la plupart récemment https://github.com/rust-lang/rust/pull/59008. (Ceux-ci ont pour la plupart été séparés de la principale demande d'extraction de génériques const .)


Quel est le statut maintenant ? Certains tests génériques const fonctionnent maintenant 🎉Cependant, il y a toujours ceux qui ne fonctionnent pas, et il y a un certain nombre de FIXME dans le code que nous devons encore traiter. Nous nous rapprochons maintenant, cependant, et nous sommes au stade où il y a des fruits à portée de main si vous voulez aider.

  • Premièrement, nous avons besoin de plus de tests. Il n'y a qu'une poignée de tests génériques const jusqu'à présent, mais nous en aimerions beaucoup plus. Pour le moment, parce que nous sommes conscients d'un certain nombre de problèmes, nous n'avons pas besoin de rapports de bogues, mais nous avons besoin de réussir les tests. Si vous proposez un cas de test qui fonctionne (et ne ressemble pas trop à un test existant), n'hésitez pas à ouvrir une pull request pour l'ajouter.
  • Deuxièmement, comme mentionné, il y a un certain nombre de FIXME(const_generics) dispersés dans le code. Nous prévoyons de les parcourir, mais @yodaldevoid et moi n'avons que peu de temps, donc si vous pensez pouvoir en aborder un, allez-y (bien que vous aimeriez peut-être laisser un commentaire en disant autant, alors nous ne ' t dupliquer les efforts).

J'ai écrit un aperçu de certains des problèmes de mise en œuvre restants avant que les génériques const ne soient prêts pour des tests appropriés, dans le post du haut, pour donner une idée approximative de ce qu'il reste à faire.

Cela a pris du temps, mais nous progressons régulièrement et espérons garder le rythme. C'est motivant de voir enfin les choses commencer à se mettre en place !

J'attends des mois pour ce post @varkor :) Je ne suis pas un sorcier de Rust mais j'aimerais m'attaquer à l'un des FIXME s

Félicitations @jplatte , @yodaldevoid et @varkor. C'est une étape énorme pour se débarrasser des traits personnalisés Array-like dans les bibliothèques mathématiques.

Aussi, pour référence de ceux qui s'intéressent aux FIXME : https://oli-obk.github.io/fixmeh/

@mark-im essayez de mettre {} autour de FOO . De cette façon, cela fonctionnera.

Citant https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md :

Lors de l'application d'une expression en tant que paramètre const (sauf pour les tableaux), qui n'est pas une expression d'identité, l'expression doit être contenue dans un bloc. Cette restriction syntaxique est nécessaire pour éviter de nécessiter une anticipation infinie lors de l'analyse d'une expression à l'intérieur d'un type.

{expression} ne devrait être nécessaire que si l'expression n'est pas un identifiant ou un littéral, c'est un bug.

Citant la même RFC :

Expression d'identité : une expression qui ne peut pas être évaluée davantage, sauf en la remplaçant par des noms dans la portée. Cela inclut tous les littéraux ainsi que tous les identifiants - par exemple 3, "Hello, world", foo_bar.

@mark-im Oui, actuellement, nous ne pouvons pas faire la différence entre les idents pour les types et les consts lors de l'analyse initiale. Nous avons repoussé la décision sur la façon de gérer tout cela dans le futur. Je suppose que maintenant pourrait être l'avenir.

Pour un peu d'arrière-plan, nous avons parlé de deux façons dont je me souviens de la façon de gérer cela. La première consiste à forcer les utilisateurs à marquer les arguments const (soit en tapant const avant eux ou d'une autre manière). Ce n'est pas génial d'un point de vue ergonomique, mais c'est facile du point de vue de l'analyse. La deuxième façon consiste à traiter tous les arguments génériques de la même manière jusqu'à ce que nous commencions à les associer aux paramètres génériques plus tard lors de la compilation. C'est probablement la méthode que nous voulons adopter, et il y a déjà eu du travail pour rendre cela possible, nous n'avons tout simplement pas franchi le pas final.

Incroyable travail. Je suis heureux de vous aider, en réparant certains des FIXME , si je peux. Je n'ai pas du tout beaucoup d'expérience avec la base de code rustc, donc je vais commencer sur le FIXME https://github.com/rust-lang/rust/blob/master/src/librustc_mir/monomorphize/item.rs# L397 , car il semble que ce serait facile.

Qu'en est-il du code suivant :

trait Foo {
    const N: usize;
    fn foo() -> [u8; Self::N];
}

Actuellement, il en résulte une erreur de compilation "aucun élément associé nommé N trouvé pour le type Self dans la portée actuelle". Un tel code sera-t-il accepté après avoir terminé les FIXME s ou nécessitera-t-il des efforts supplémentaires pour être mis en œuvre ?

@yodaldevoid

Petite question, désolé si cela a déjà été discuté.

La deuxième façon consiste à traiter tous les arguments génériques de la même manière jusqu'à ce que nous commencions à les associer aux paramètres génériques plus tard lors de la compilation. C'est probablement la méthode que nous voulons adopter, et il y a déjà eu du travail pour rendre cela possible, nous n'avons tout simplement pas franchi le pas final.

Cela ne va-t-il pas plutôt à contre-courant sur le principe que Rust prend en explicite les signatures de fonctions pour éviter de produire des erreurs liées à l'implémentation d'une fonction ? Peut-être que je me trompe totalement et que vous parlez de l'analyse de paramètres génériques dans le corps d'une fonction.

Cela ne va-t-il pas plutôt à contre-courant sur le principe que Rust prend en explicite les signatures de fonctions pour éviter de produire des erreurs liées à l'implémentation d'une fonction ? Peut-être que je me trompe totalement et que vous parlez de l'analyse de paramètres génériques dans le corps d'une fonction.

Il s'agit de déterminer si les identifiants passés en tant que paramètres génériques à une fonction sont des constantes ou des types.

Exemple:

fn greet<const NAME:&'static str>(){
    println!("Hello, {}.",NAME);
}
const HIS_NAME:&'static str="John";
greet::<HIS_NAME>();
greet::<"Dave">();

Notez que lors de la définition de la fonction, vous devez spécifier que NAME est une constante, mais lorsque vous l'appelez, il n'est pas nécessaire de dire que HIS_NAME est une constante au sein de l'opérateur turbofish ( ::< > ) .

@zesterer Si cette solution proposée était rendue transparente pour l'utilisateur (par exemple, lors de la définition d'une fonction, il n'y avait pas de différence entre les types de paramètres), vous auriez raison. Cependant, l'idée derrière la solution proposée n'est pas de changer le comportement face à l'utilisateur, mais plutôt de changer uniquement l'implémentation. L'utilisateur écrirait toujours des signatures de fonction avec des paramètres explicites de type, de const et de durée de vie, et les arguments génériques devraient toujours correspondre à un paramètre correspondant, la façon dont nous l'analysons dans le compilateur changerait.

@newpavlov Je suis surpris que le code ne fonctionne pas. Si vous pouviez soumettre un problème séparé, ce serait apprécié.

@yodaldevoid @robarnold

Ah, je t'ai. J'ai supposé à tort qu'il s'agissait de signatures de type/fonction.

Pour utiliser les génériques const sur les types de tableaux intégrés, j'ai ouvert #60466 pour voir les opinions des autres à ce sujet.

Les blocs Impl semblent complètement cassés pour le moment. Est-ce prévu dans l'état actuel de la fonctionnalité, ou dois-je ouvrir un autre problème à ce sujet ?

Les blocs Impl semblent complètement cassés pour le moment. Est-ce prévu dans l'état actuel de la fonctionnalité, ou dois-je ouvrir un autre problème à ce sujet ?

Vous devez ajouter des {} s autour de N , impl<const N: usize> Dummy<{N}> {} .

Mais oui, alors vous obtiendrez une erreur sur le paramètre sans contrainte.

Ah merci. J'ai oublié le truc des bretelles !

Je ne suis pas surpris qu'il échoue une fois que cela est résolu, car il s'agissait d'un cas de test fortement minimisé.

...mais oui, celui-ci devrait probablement fonctionner :

#![feature(const_generics)]

trait Dummy {}

struct Vector<const N: usize> {
    data: [f32; N],
}

impl<const N: usize> Dummy for Vector<{N}> {}

... et cela échoue toujours avec E0207 :

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:9:12
  |
9 | impl<const N: usize> Dummy for Vector<{N}> {}
  |            ^ unconstrained const parameter

error: aborting due to previous error

For more information about this error, try `rustc --explain E0207`.
error: Could not compile `small-matrix`.

To learn more, run the command again with --verbose.

... et je suppose que c'est ce que @varkor voulait dire par "problèmes de gestion des tableaux pour les génériques consts" dans # 60466 :

#![feature(const_generics)]

fn dummy<const N: usize>() -> [f32; N] {
    [0.; N]
}

->

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: cat_expr Errd
 --> src/lib.rs:3:44
  |
3 |   pub fn dummy<const N: usize>() -> [f32; N] {
  |  ____________________________________________^
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: cat_expr Errd
 --> src/lib.rs:4:5
  |
4 |     [0.; N]
  |     ^^^^^^^

error: internal compiler error: QualifyAndPromoteConstants: Mir had errors
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) ("return type"): bad type [type error]
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) (LocalDecl { mutability: Mut, is_user_variable: None, internal: false, is_block_tail: None, ty: [type error], user_ty: UserTypeProjections { contents: [] }, name: None, source_info: SourceInfo { span: src/lib.rs:3:1: 5:2, scope: scope[0] }, visibility_scope: scope[0] }): bad type [type error]
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:356:17
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.36.0-nightly (cfdc84a00 2019-05-07) running on x86_64-unknown-linux-gnu

note: compiler flags: -C debuginfo=2 -C incremental --crate-type lib

note: some of the compiler flags provided by cargo are hidden

@HadrienG2
Voir #60619 et #60632.

Je pense que ce code devrait compiler :
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e45b7b5e881732ad80b7015fc2d3795c

mais il fait actuellement des erreurs :

error[E0119] : implémentations conflictuelles du trait std::convert::TryFrom<[type error]> pour le type MyArray<_, _> :

Malheureusement, c'est une restriction de Try{From,Into} et n'a rien à voir avec les génériques const. Ils ne peuvent pas être mises en œuvre génériquement dans de nombreux cas: parc à

@oberien dans votre exemple, T est un type étranger, et on ne sait pas si T: Into<Foo<T>> tient ou non.

Dans mon exemple, [T; N] est un type défini dans libcore et vaut #[fundamental] (je ne sais même pas ce que cela signifie), donc je pense que le résolveur de traits sait en fait que [T; N]: Into<MyArray<T, {N}>> ne tient pas, et là ne devrait pas être un conflit, je pense?

[...] et vaut #[fundamental] [...]

C'est le problème, fundamental est lié au fait de permettre aux utilisateurs en aval de définir des implémentations de traits lorsqu'ils encapsulent un type local dans le type de wrapper fondamental. Vous pouvez voir dans ce terrain de jeu que votre implémentation fonctionne entre des types non fondamentaux, mais échoue si le type from est marqué fundamental (avec un bien meilleur message d'erreur).

@varkor @yodaldevoid Donc, je viens de me retrouver dans une situation où S<{N == 0}> (avec S prenant un paramètre const bool et N un const usize ) n'est ni S<{true}> ou S<{false}> dans l'œil du compilateur (dans le sens où il n'implémente pas les mêmes traits que l'un ou l'autre), et je ne sais pas s'il s'agit d'un bogue ou d'un limitation attendue du prototype actuel. Pouvez-vous me donner un bref rappel sur la logique actuelle de l'unification ?

A titre d'exemple minimisé, ceci...

// Machinery for type level if-then-else
struct Test<const B: bool>;
trait IfFn<S, Z> { type Out; }
impl<S, Z> IfFn<S, Z> for Test<{true }> { type Out = S; }
impl<S, Z> IfFn<S, Z> for Test<{false}> { type Out = Z; }

// Returns an u8 if B is true, else an u16
fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
    0
}

... produit l'erreur suivante :

error[E0277]: the trait bound `Test<B>: IfFn<u8, u16>` is not satisfied
  --> src/lib.rs:32:1
   |
32 | / fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
33 | |     0
34 | | }
   | |_^ the trait `IfFn<u8, u16>` is not implemented for `Test<B>`
   |
   = help: the following implementations were found:
             <Test<false> as IfFn<S, Z>>
             <Test<true> as IfFn<S, Z>>

Je ne sais pas vraiment, mais je pense que le problème peut être que le compilateur n'a pas la logique pour décider si le littéral 0 est un u8 ou un u16 . Essayez quelque chose comme : 0u8 as _

Ce n'est pas ça, le message d'erreur reste le même.

Mais si vous voulez une version qui n'implique pas d'inférence de type entier, voici une minimisation plus stupide :

struct Test<const B: bool>;
trait Not { const B: bool; }
impl Not for Test<{true }> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }

fn should_be_ok<const B: bool>() -> bool {
    <Test<{B}> as Not>::B
}

Échec toujours car Test<{B}> n'a prétendument pas implémenté Not .

Salut ! Je ne sais pas vraiment si l'unification sur les valeurs const est censée fonctionner ou non, mais il existe un nouveau système d'unification en cours ("craie") qui est en cours d'élaboration, mais ce n'est pas ce que Rustc utilise actuellement.
Donc, ce que vous essayez de faire _peut_ devoir attendre que la craie soit prête. Et même alors, je ne suis pas sûr que cela fonctionnera ou qu'il soit censé fonctionner même avec de la craie.
Voir https://github.com/rust-lang/rust/issues/48049 pour la progression.

@HadrienG2 pourriez-vous déposer un problème séparé pour cela ? Je ne suis pas tout à fait surpris que cela ne fonctionne pas pour le moment en raison de quelques autres problèmes que nous examinons, mais il s'agit d'un nouveau cas et j'aimerais qu'il soit suivi.

@carado L' unification est effectuée sur consts ou rien de tout cela ne fonctionnerait. Les génériques de Const ne sont pas bloqués à la craie.

@HadrienG2 une fois que nous aurons une spécialisation pour les génériques const, vous pourrez faire quelque chose comme

struct Test<const B: bool>;
trait Not { const B: bool; }
impl<const B: bool> Not for Test<B> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }

fn should_be_ok<const B: bool>() -> bool {
    <Test<{B}> as Not>::B
}

Ce que vous voulez faire, c'est une sorte de contrôle d'exhaustivité comme il y en a pour la correspondance qui vient d'être étendue au système de traits. Je ne sais pas si cela est mentionné dans aucun fil.

@yodaldevoid Classé comme https://github.com/rust-lang/rust/issues/61537 .

@carado Chalk ne concerne pas les "primitifs de typesystem" réels, dont celui-ci fait partie.
C'est-à-dire que rustc doit encore (même aujourd'hui, puisque Chalk est déjà partiellement intégré) implémenter la partie de l'unification qui traite des entités opaques à Chalk (telles que deux expressions constantes différentes).
Si nous implémentons cela (pour lequel nous n'avons aucun plan pour un avenir proche, de toute façon), cela ne changera pas beaucoup même après que Chalk ait remplacé le système de traits (qui est son objectif principal, pas l' unification).

Oh, mon mauvais alors ! Je suppose que je ne sais pas vraiment de quoi je parle.

L'une des choses impressionnantes que constgenerics fournirait est des fonctions simples calculées au moment de la compilation, par exemple factorielles.

Exemple:

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<i - 1>().unwrap() + i)
    }
}

Autant que je sache, il existe une prise en charge limitée des génériques const dans nightly ? Si c'est le cas, y a-t-il un endroit plus organisé que ce numéro où je peux trouver comment l'utiliser et tout ?

Les fonctions @dancojocaru2000 const devraient être le moyen préféré pour le calcul au niveau de la valeur au moment de la compilation, par exemple https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4994b7ca9cda0bfc44f5359443431378 , ce qui se produit ne pas fonctionner car ils ne sont pas encore entièrement mis en œuvre. Mais les const ne sont pas non plus des génériques.

Votre extrait peut être problématique car je ne sais pas si match est censé fonctionner pour les arguments const . Vous avez également mélangé l'addition avec la multiplication dans le code, mais cela n'a pas trop d'importance.

Je pense que vous pouvez déjà faire des factorielles au moment de la compilation avec un encodage Peano, qui est souvent présenté comme une démo pour d'autres langages fonctionnels.

temps de compilation calculé fonctions simples

Je pense que la fonctionnalité la plus appropriée serait const fn .

Théoriquement, votre code fonctionnerait presque tel quel

#![feature(const_generics)]

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<{X - 1}>().unwrap() + i)
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Mais ce n'est pas le cas :

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/main.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`

Bien que vous deviez vraiment utiliser un type non signé :

#![feature(const_generics)]

fn factorial<const X: u32>() -> u32 {
    match X {
        0 => 1,
        1 => 1,
        _ => factorial::<{ X - 1 }>() + X,
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

y a-t-il un endroit plus organisé que ce problème où je peux trouver comment l'utiliser et tout ça ?

C'est généralement couvert par le livre instable , mais il n'y a rien d'utile maintenant. Au fur et à mesure que vous découvrez des morceaux, vous pourriez peut-être envisager de commencer à esquisser du contenu pour cela ?

factorielles à la compilation avec un encodage Peano,

Pour un exemple de cela, voir la caisse typenum

Théoriquement, votre code fonctionnerait presque tel quel

Lorsque vous appelez factorial::<0> le _ => factorial::<{X - 1}>() * X serait également codé, n'est-ce pas ? Mais tenter de le faire provoquerait un dépassement d'entier.

Puis-je m'attendre à ce qu'un code comme celui-ci soit compilé à l'avenir ?

#![feature(const_generics)]

trait NeedsDrop<const B: bool> { }
impl<T> NeedsDrop<std::mem::needs_drop<T>()> for T { }

fn foo<D: NeedsDrop<false>>(d: D) { }

Il s'agit d'un croisement avec une implémentation récente de certains tableaux [T; N] pour N <= 32 tant que génériques const, mais le code suivant ne compile pas sur un dernier ( 4bb6b4a5e 2019-07-11 ) tous les soirs avec diverses erreurs the trait `std::array::LengthAtMost32` is not implemented for `[u64; _]`

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
    const N: usize
>(pub [u64; N]) 
where [u64; N]: std::array::LengthAtMost32, 
   [u64; N*2]: std::array::LengthAtMost32;

bien que ce qui suit :

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
    const N: usize
>(pub [u64; N]) 
where [u64; N]: std::array::LengthAtMost32;

On dirait que N*2 n'est pas un const ou un qualificatif valide pour une telle contrainte

@shamatar N*2 doit être entouré d'accolades comme `[u64; {N*2}]

Je pense que les erreurs « trait non implémenté » proviennent des macros dérivées n'ajoutant pas la limite pour [u64; {N*2}] , mais si les dérivés sont supprimés, il existe actuellement un ICE :

```error : erreur interne du compilateur : la constante de type a une erreur ignorée : TooGeneric
--> src/main.rs:5:1
|
5 | / structure pub BigintRepresentation<
6 | | const N : utiliser la taille
7 | | >(pub [u64; N])
8 | | où [u64; N] : std::array::LengthAtMost32,
9 | | [u64; {N*2}] : std::array::LengthAtMost32 ;
| |______________________________________________^

le thread 'rustc' a paniqué à 'aucune erreur rencontrée même si delay_span_bug émis', src/librustc_errors/lib.rs:366:17
note : exécutez avec la variable d'environnement RUST_BACKTRACE=1 pour afficher une trace.

Ce code ne compile pas :

#![feature(const_generics)]

struct Foo<const X: usize>([u8; X]);

impl<const X: usize> Foo<X> {
    fn new() -> Self {
        Self([0u8; X])
    }
}
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error[E0573]: expected type, found const parameter `X`
 --> src/lib.rs:5:26
  |
5 | impl<const X: usize> Foo<X> {
  |                          ^
  |                          |
  |                          not a type
  |                          help: try using the variant's enum: `regex_syntax::ast::HexLiteralKind`

error[E0107]: wrong number of const arguments: expected 1, found 0
 --> src/lib.rs:5:22
  |
5 | impl<const X: usize> Foo<X> {
  |                      ^^^^^^ expected 1 const argument

error[E0107]: wrong number of type arguments: expected 0, found 1
 --> src/lib.rs:5:26
  |
5 | impl<const X: usize> Foo<X> {
  |                          ^ unexpected type argument

error: aborting due to 3 previous errors

@npmccallum vous devez faire Foo<{X}>

@pengowen123 Oui, il compile (plante le compilateur avec constant in type had an ignored error: TooGeneric ) sans derive , mais cela peut alors être une question distincte de savoir si une telle "double contrainte" est effectivement N <= 32 et N <= 16 ne sont pas appliqués par le compilateur.

Les expressions qui mentionnent des paramètres ne fonctionneront probablement pas pendant un certain temps, [T; N] et Foo<{N}> sont des cas spéciaux pour ne pas avoir d'expressions avec une mention de N , mais plutôt référez-vous directement au paramètre N , en contournant le problème plus large.

L'utilisation de Self provoque un plantage.
Même en l'échangeant contre Value<{C}> , il plante toujours.

#![feature(const_generics)]

struct Value<const C: usize>;

impl<const C: usize> Value<{C}> {
    pub fn new() -> Self {
        unimplemented!()
    }
}

pub fn main() {
    let value = Value::new();
}

Identique à #61338, la source du problème est la compilation incrémentielle.

temps de compilation calculé fonctions simples

Je pense que la fonctionnalité la plus appropriée serait const fn .

Théoriquement, votre code fonctionnerait presque tel quel

#![feature(const_generics)]

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<{X - 1}>().unwrap() + i)
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Mais ce n'est pas le cas :

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/main.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`

Bien que vous deviez vraiment utiliser un type non signé :

#![feature(const_generics)]

fn factorial<const X: u32>() -> u32 {
    match X {
        0 => 1,
        1 => 1,
        _ => factorial::<{ X - 1 }>() + X,
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Cette fonctionnalité est utile pour moi aussi, const fn ne suffit pas. Je veux l'utiliser pour un tableau à N dimensions avec une condition de terminaison de dimensions=0.

Je suis capable de créer une structure :

#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
    real_array: [T; len]
}

impl<T: Default, const len: usize> MyArray<T, {len}> {
    fn new() -> Self {
        return MyArray {
            real_array: [Default::default(); len]
        }
    }
}

Cela échoue car je ne peux pas réellement initialiser le tableau en raison de :

error: array lengths can't depend on generic parameters
 --> src/main.rs:9:46
  |
9 |             real_array: [Default::default(); len]
  |                                              

J'ai essayé de contourner ce problème en définissant les valeurs const sur la valeur générique.

Cela dépasse l'erreur ci-dessus, mais le compilateur se bloque alors.

error: internal compiler error: src/librustc/ty/subst.rs:597: const parameter `height/#0` (Const { ty: usize, val: Param(height/#0) }/0) out of range when substituting substs=[]

Le langage a désespérément besoin de génériques constants de base, de sorte que la bibliothèque standard n'ait pas à définir chaque fonction pour chaque taille de tableau. C'est la seule raison pour laquelle je n'utilise pas de rouille. Avons-nous vraiment besoin de fonctions complètes de compilation ?
Je me trompe peut-être, mais de simples expressions entières devraient suffire et j'espère que personne ne perd son temps à s'assurer que ces exemples farfelus fonctionnent.

Le langage a désespérément besoin de génériques constants de base, de sorte que la bibliothèque standard n'ait pas à définir chaque fonction pour chaque taille de tableau

Il y a déjà des efforts là-bas https://github.com/rust-lang/rust/issues/61415.

J'espère que personne ne perd son temps à s'assurer que ces exemples farfelus fonctionnent.

Certaines personnes le font, je ne vois pas de problème avec ça ;)

C'est la seule raison pour laquelle je n'utilise pas de rouille.

C'est la raison la moins intéressante que j'aie vue citée pour quelqu'un qui n'utilise pas de rouille. Êtes-vous sûr de dire la vérité?

mais de simples expressions entières devraient suffire

Faire même cette portée réduite est incroyablement difficile. Essayez-le, nous avons des gens vraiment compétents pour le faire et il y a une raison pour laquelle cela prend autant de temps.

Malheureusement, je n'ai pas pu consacrer beaucoup de temps à la correction des bugs génériques de const récemment (ou à Rust plus généralement). Si quelqu'un veut s'impliquer dans la promotion des génériques const, je serais heureux de donner des conseils pour résoudre les problèmes ouverts et revoir les corrections de bogues, même s'il faudra probablement un peu de temps avant que je puisse à nouveau me concentrer sur ce sujet.

J'espère que personne ne perd son temps à s'assurer que ces exemples farfelus fonctionnent.

Personne ne l'est, miri est déjà bien plus puissant que C++ constexpr .
Et personne ne travaille sur quelque chose d'extraordinaire comme dériver N = M de N + 1 = M + 1 .

La plupart de ces bogues ne concernent pas les expressions qu'ils contiennent, ils concernent le système de types et la façon dont les const génériques interagissent avec toutes les autres fonctionnalités.
Ils seraient toujours là si vous n'aviez que des const génériques et des entiers littéraux.

Btw, je pense que l'erreur "les longueurs des tableaux ne peuvent pas dépendre des paramètres génériques" pour les expressions [expr; N] ne sont pas nécessaires, nous pourrions utiliser la même astuce que nous faisons pour les types [T; N] , et tirer le N sans l'évaluer en tant qu'expression.

Bien que je veuille tenter le coup, je ne suis pas sûr d'être la bonne personne. J'ai utilisé peu de rouille et je connais très peu la théorie du compilateur. J'ai peut-être besoin d'un peu de coaching, mais je suis certainement prêt. ??

Edit: J'ai cependant une bonne expérience des logiciels en général.

@varkor , je cherchais quelque chose d'utile à faire sur rustc , et j'adorerais intervenir et aider. Merci également d'avoir été franc sur votre capacité à y consacrer du temps !

@varkor , je suis en train de

Une chose que nous pourrions faire maintenant, je viens de réaliser, c'est d'autoriser les expressions Foo::<{...}> / [expr; ...] à faire référence à des paramètres génériques (dans la partie ... ).

En effet, les expressions doivent être imbriquées quelque part dans un corps, ce qui tend à empêcher les dépendances cycliques.

Cependant, je crains qu'avoir par exemple [(); [0; 1][0]] dans une signature ne se brise, donc nous aurions probablement besoin d'une course de cratère de toute façon (et de nos jours, cela prend une éternité).

Pour tous ceux qui souhaitent aider avec les génériques const, mon conseil serait de jeter un coup d'œil à la liste des problèmes de génériques const ouverts et d'enquêter sur celui qui vous semble intéressant. Certains ont déjà eu une enquête, qui devrait être dans les commentaires. La plupart des problèmes vont probablement nécessiter un peu de creusement. Il est utile de faire un commentaire si vous envisagez d'enquêter sur quelque chose, afin que nous ne dupliquions pas les efforts (mais vous pouvez souvent ignorer le responsable du problème s'il n'y a pas eu d'activité depuis un certain temps). Si vous avez des questions, vous pouvez les poser dans les commentaires du numéro, ou sur Discord ou Zulip. @eddyb , @yodaldevoid , @oli-obk et moi connaissons bien de nombreux domaines pertinents et sommes de bonnes personnes à qui poser la question. Merci pour tout votre intérêt !

cc @hameerabbasi , @ranweiler

Questions sur les génériques const :

  1. Où puis-je trouver de la documentation (pour ne pas avoir à poser de questions ici)
  2. Quelles limitations existent en ce moment sur l'utilisation d'un paramètre const de type arbitraire ?
  3. Quelles fonctionnalités importantes des const génériques ne sont pas implémentées / font planter le compilateur ?

PS (Contributeurs :) Merci beaucoup d'avoir travaillé sur cette fonctionnalité.

const-generics est toujours en cours de développement, il n'y a donc pas encore de documentation officielle à ce sujet. Je ne suis pas trop sûr pour les deux autres questions. La dernière fois que j'ai utilisé const-generics, ils se sont écrasés lorsque j'ai spécifié certains paramètres de const-generic, mais cela fait près d'un mois que je n'ai rien fait avec, donc les choses ont peut-être changé depuis lors.

Et personne ne travaille sur quelque chose d'extraordinaire comme dériver N = M de N + 1 = M + 1 .

Ce serait très utile d'avoir un solveur pour de telles contraintes de type. Par exemple, lors de la mise en œuvre d'un ajout si deux nombres de N bits, la taille de retour doit être (N + 1) pour tenir compte d'un bit de débordement. Lorsque (par exemple) deux nombres de 5 bits sont donnés, le solveur doit vérifier que N + 1 = 6 . Espérons que cela puisse être boulonné sur les génériques const plus tard :)

@ flip111 Oui, je pense que le plan est d'ajouter cela plus tard, mais ce type de contraintes d'expression générales est très complexe et difficile à mettre en œuvre. Nous ne les verrons donc peut-être pas avant au moins quelques années.

Pour être honnête, résoudre ce genre de problèmes basés sur des contraintes ressemble beaucoup à un travail pour Prolog. Peut-être que le ferroutage sur le moteur à craie est une option ? Afaik, cela résout les Traits, n'est-ce pas ?

Au fait, j'adore le sujet, même si je ne peux pas me permettre d'aider avec ce guichet automatique. Merci à tous ceux qui travaillent sur Const Generics pour votre temps et votre engagement. ??

Une petite mise à jour : je travaille mon chemin jusqu'à l'arbre de rouille. J'ai l'intention de contribuer, mais je veux apprendre par moi-même à un endroit où je n'ai pas besoin d'un encadrement excessif.

Je suis capable de créer une structure :

#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
    real_array: [T; len]
}

impl<T: Default, const len: usize> MyArray<T, {len}> {
    fn new() -> Self {
        return MyArray {
            real_array: [Default::default(); len]
        }
    }
}

Cela échoue car je ne peux pas réellement initialiser le tableau en raison de :

error: array lengths can't depend on generic parameters
 --> src/main.rs:9:46
  |
9 |             real_array: [Default::default(); len]
  |                                              

J'ai rencontré le même problème, mais j'ai trouvé une solution de contournement en utilisant MaybeUninit :
https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=3100d5f7a4efd844954a6fa5e8b8c526
C'est évidemment juste une solution de contournement pour obtenir des tableaux correctement initialisés, mais pour moi, cela est suffisant jusqu'à ce qu'un moyen approprié soit disponible.
Remarque : je pense que le code devrait toujours fonctionner comme prévu, mais si quelqu'un trouve un bogue avec l'utilisation de l'unsafe, je serais heureux de le corriger.

@raidétait que vous = pour initialiser la mémoire non initialisée. Faites ceci à la place,

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5d962ce7c553e850030240244608ec00

@KrishnaSannasi merci, bonne prise :D (techniquement, je n'ai pas supprimé de mémoire non initialisée car je ne l'utilise que pour les primitives, mais c'est bien d'avoir une version correcte ici)

Techniquement, laisser tomber même les flottants n'est pas défini, mais ce n'est pas exploitable pour le moment.

Techniquement, laisser tomber même les flottants n'est pas défini, mais ce n'est pas exploitable pour le moment.

Je me serais attendu à ce que la suppression de tout type Copy , même non initialisé, soit une opération interdite. Cela vaut peut-être la peine de changer.

Techniquement, laisser tomber même les flottants n'est pas défini, mais ce n'est pas exploitable pour le moment.

Je me serais attendu à ce que la suppression de tout type Copy , même non initialisé, soit une opération interdite. Cela vaut peut-être la peine de changer.

C'est toujours techniquement UB. Bien que supprimer en toute sécurité les valeurs Copy définies soit une opération interdite, le compilateur peut décider de faire des optimisations inattendues si vous essayez de supprimer la mémoire non initialisée de n'importe quel type (par exemple, supprimez tout le code qui touche éventuellement cette valeur), ce qui pourrait éventuellement casser des choses. C'est ma compréhension.

Je ne veux pas être impoli, mais ≥ 60 personnes sont notifiées des commentaires sur ce fil, nous devrions donc probablement limiter les discussions hors sujet au strict minimum. Profitons-en plutôt pour coordonner le travail sur les génériques const car c'est ce que nous voulons tous arriver.

Crash si j'utilisais des _aliases pointant vers le type avec const-param_ depuis une autre caisse (dépendance).

Par exemple:

  • caisse A, lib
    ```#![crate_type = "lib"]

    ![feature(const_generics)]

Type de pub Alias= Structure;
structure de pub(T);

- crate B

caisse externe caisse_a;
utilisez crate_a::Alias ;

pub fn inner_fn (v : alias) {}
```
crash-log (44580).txt

@fzzr- ressemble à #64730

J'ai changé du code pour des génériques const et il semble qu'il y ait vraiment deux cas d'utilisation différents. Je ne sais pas s'ils devraient être confondus ou si nous serions mieux servis avec deux syntaxes différentes pour les cas d'utilisation.

Beaucoup de mes utilisations ne sont pas réellement pour les types où une valeur constante joue un rôle dans la détermination des types, mais plutôt pour me prévaloir de fonctionnalités qui sont verrouillées pour les valeurs non constantes/littérales (toujours pas entièrement prises en charge, par exemple les modèles de correspondance, mais en fin de compte devra l'être).

À mon humble avis, nous devrions officiellement débarquer des "arguments const" aux côtés des génériques const, afin que les gens n'écrivent pas de code mutant introduisant un millier de "génériques const" (un pour chaque argument) pour que le compilateur évalue certaines variables en tant que littéraux/constantes.

@mqudsi Pouvez-vous donner un exemple ? Il existe déjà des plans et des travaux fondamentaux en cours pour rendre Const eval plus puissant. C'est orthogonal aux génériques const, cependant.

Ce que je veux dire, c'est que si vous souhaitez refactoriser le code d'analyseur typique comme le suivant pour le réutiliser :

let mut result: u32 = 0;
let mut digits = 0;
while digits < max_digits && src.has_remaining() {
    match src.get_u8() {
        d<strong i="6">@b</strong>'0'..=b'9' => {
            result = result*10 + (d - b'0') as u32;
            digits += 1;
        },
        b'>' => match result {
            0..=191 => break, // valid range
            _ => return Err(DecoderError::OutOfRange),
        },
        _ => return Err(DecoderError::Malformed)
    }
}

Vous le déplaceriez généralement vers une fonction, sauf que ce qui suit ne fonctionnera pas car certaines valeurs utilisées dans la correspondance de modèles sont devenues des variables :

#[inline(always)]
fn read_str_digits<B: bytes::buf::Buf>(src: &mut B, stop_word: u8, 
    max_digits: u8, min_value: u32, max_value: u32) -> Result<u32, DecoderError> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="10">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            stop_word => match result {
                min_value..=max_value => break, // valid range
                _ => return Err(DecoderError::OutOfRange),
            },
            _ => return Err(DecoderError::Malformed)
        }
    }

    ...
}

Si les génériques const atterrissent avant les arguments const, je peux soudainement abuser des génériques const pour arriver à ce qui suit :

#[inline(always)]
fn read_str_digits<const MinValue: u32, const MaxValue: u32, const StopWord: u8, B: bytes::buf::Buf>
(src: &mut B, max_digits: u8) -> Result<u32, DecoderError> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="14">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            StopWord => match result {
                MinValue..=MaxValue => break, // valid range
                _ => return Err(DecoderError::OutOfRange),
            },
            _ => return Err(DecoderError::Malformed)
        }
    }

    ...
}

À ce jour, même ce code ne fonctionnera pas car le compilateur ne détecte pas que les valeurs génériques const sont des constantes valides pour une utilisation dans un modèle, mais cela est évidemment incorrect et doit être corrigé avant que RFC 2000 puisse atterrir. Ne vous méprenez pas, je me bats pour des constantes génériques depuis des années et j'ai des PR prêts à partir pour une douzaine de caisses majeures (j'ai hâte de faire le fuseau horaire en chrono un générique const et unifier tous les différents types DateTime ), mais à mon humble avis s'il devient possible d'abuser des génériques const pour falsifier les arguments const (où par "const" ce que nous entendons vraiment est "littéral") alors vous allez voir généralisé abuser de ça.

Ce n'est pas nécessairement la fin du monde, mais sans avoir creusé trop profondément, il semble qu'une implémentation appropriée et complète de génériques const inclura nécessairement toute la plomberie pour les arguments const de toute façon, alors autant prendre le temps supplémentaire pour finaliser la syntaxe/ux/story pour les arguments const pendant que nous y sommes et éviter une ère malheureuse de code puant. Oui, ce qui précède peut toujours être fait avec des macros, mais l'ergonomie des génériques const est mille fois plus simple.

fwiw, voici à quoi j'imagine que la version de l'argument const ressemblera:

#[inline(always)]
fn read_str_digits<B: Bytes>(src: &mut B, min_value: const u32, 
    max_value: const u32, stop_word: const u8, max_digits: u8) -> Result<u32, ()> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="23">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            stop_word => match result {
                min_value..=max_value => break, // valid range
                _ => return Err(()),
            },
            _ => return Err(())
        }
    }

    ...
}

Ce serait pratiquement identique aux génériques const mais pour la sémantique.

Vous le déplaceriez généralement vers une fonction, sauf que ce qui suit ne fonctionnera pas car certaines valeurs utilisées dans la correspondance de modèles sont devenues des variables :

Cela semble être un cas d'utilisation qui est plus facilement traité en permettant aux valeurs des variables liées d'être utilisées dans des modèles (pas seulement des consts), avec une nouvelle syntaxe. Le fait que les consts soient actuellement autorisés dans les modèles est contre-intuitif et, pour autant que je sache, historique.

@mqudsi Pardonnez-moi si c'est une question idiote, mais je ne vois rien de mal dans l'exemple que vous avez donné. Cela ressemble à un cas d'utilisation parfaitement valide pour les génériques const : avoir une définition qui est généralisée pour fonctionner avec des valeurs arbitraires comme max/min. Je ne vois pas vraiment les avantages des arguments const par rapport aux génériques const. Ils me semblent équivalents ; c'est-à-dire que les arguments const pourraient simplement être implémentés comme une suppression des génériques const. Pourriez-vous nous en dire plus sur ce qui ne va pas avec ce modèle de conception ?

Voici un résumé du travail sur les génériques const depuis la dernière mise à jour . La dernière fois, il s'agissait de l'implémentation principale : s'assurer que tout s'assemblait et réussir certains des cas de test de base. Depuis lors, l'effort s'est concentré sur la fiabilisation des génériques const : la correction des cas qui faisaient planter le compilateur, ou ne fonctionnaient pas de manière inattendue, ou l'amélioration des diagnostics. Nous nous rapprochons de quelque chose qui fonctionne de manière fiable, même s'il reste encore du chemin à parcourir.


  • @ skinny121 a fait un travail fantastique au cours du mois dernier pour résoudre divers problèmes en suspens avec les génériques const :

    • Extension de la suite de tests (https://github.com/rust-lang/rust/pull/60550)

    • Correction des problèmes d'inférence de type (https://github.com/rust-lang/rust/pull/64679, https://github.com/rust-lang/rust/pull/65579)

    • Prise en charge de plus de types dans les génériques const, y compris les chaînes et les tranches (https://github.com/rust-lang/rust/pull/64858) et les pointeurs (https://github.com/rust-lang/rust/pull/64986 ) (bien que notez que ceux-ci ne seront probablement pas stabilisés immédiatement avec le reste des génériques const, car ils ne sont pas pris en charge dans la RFC d'origine )

    • Correction des problèmes de diagnostic avec les génériques const (https://github.com/rust-lang/rust/pull/65154, https://github.com/rust-lang/rust/pull/65579)

    • Correction d'un certain nombre de problèmes liés à l'utilisation de cross-crate génériques const (https://github.com/rust-lang/rust/pull/65365)

  • @eddyb a corrigé quelques problèmes avec l'évaluation const affectant les génériques const (https://github.com/rust-lang/rust/pull/63497)
  • @matthewjasper a corrigé certains problèmes liés à l'utilisation de génériques const dans les macros (https://github.com/rust-lang/rust/pull/63083)
  • @davidtwco a corrigé un problème impliquant les génériques et les constructeurs const (https://github.com/rust-lang/rust/pull/60839)
  • @GuillaumeGomez a corrigé l'affichage des génériques const dans Rustdoc (https://github.com/rust-lang/rust/pull/61605)
  • @varkor a corrigé certains problèmes d'inférence de type (https://github.com/rust-lang/rust/pull/61570, https://github.com/rust-lang/rust/pull/60742, https://github. com/rust-lang/rust/pull/60508), un problème limitant les endroits où les génériques const pourraient être utilisés (https://github.com/rust-lang/rust/pull/60717) et divers plantages du compilateur (https://github.com/rust-lang/rust/pull/60717) /github.com/rust-lang/rust/pull/61380, https://github.com/rust-lang/rust/pull/61333, https://github.com/rust-lang/rust/pull/60710 )

De plus, d'autres travaux sur le compilateur ont fini par résoudre certains des autres problèmes liés aux génériques de const.

Nous avons également commencé à utiliser des génériques const à l'intérieur du compilateur lui-même : les implémentations de traits de tableau utilisent désormais des génériques const grâce au travail de @crlf0710 et @scottmcm (https://github.com/rust-lang/rust/pull/60466, https://github.com/rust-lang/rust/pull/60466, https://github.com/rust-lang/rust/pull/60466 ://github.com/rust-lang/rust/pull/62435), qui a conduit à des améliorations de performances et qui nous permettra également de délimiter les implémentations de traits de tableau à l'avenir (lorsque les génériques const seront stabilisés). @Centril a utilisé la même approche pour améliorer VecDeque (https://github.com/rust-lang/rust/pull/63061).


De nombreux bogues particulièrement courants avec les génériques const ont été corrigés au cours des derniers mois. @nikomatsakis étudie la normalisation paresseuse , qui devrait résoudre une multitude de problèmes restants, tandis que @jplatte cherche à corriger la désambiguïsation des paramètres const des paramètres de type (vous n'aurez donc plus à taper {X} pour un argument const).

Je tiens également à remercier @eddyb pour son mentorat et sa révision des génériques const tout au long du développement, ce qui a été inestimable.

Il y a encore un certain nombre d'autres questions à aborder et, comme auparavant, nous pourrions utiliser toute l'aide que nous pouvons obtenir - si vous êtes intéressé par la lutte contre l' une des questions en

@mqudsi @mark-im Serait-il approprié d'étendre l'alternative syntaxique actuelle pour les génériques, impl Trait en position d'argument, aux génériques const ? C'est la syntaxe @mqudsi suggérée pour ce cas d'utilisation.

Personnellement, je pense que cela améliorerait la lisibilité de tels cas d'utilisation, mais je vois un problème : les génériques normaux sont utilisés pour transmettre des données, ils sont donc liés à un argument. Ce n'est pas le cas pour les génériques const, alors comment les passeriez-vous ? Prétendre qu'il s'agit d'arguments ?
Dans le cas de impl Trait en position d'argument, il ne peut pas non plus être utilisé avec la syntaxe turbofish. Pour const en argument, ce serait l'inverse. Cela peut être déroutant.

Je ne sais pas si les avantages l'emportent sur la "bizarrerie" ici, mais je voulais quand même partager mes réflexions.

Je me suis vaguement rappelé une discussion à ce sujet auparavant, et maintenant je l'ai trouvée : https://internals.rust-lang.org/t/pre-rfc-const-function-arguments/6709

@PvdBerg1998 cela semble être une très mauvaise idée. Je ne comprends vraiment pas que <…> soit une _syntaxe difficile_ à lire. Deux possibilités ici :

  1. Vous pensez, comme moi, que ce n'est pas difficile à lire, même avec plusieurs bornes.
  2. Vous pensez que c'est difficile à lire : alors peut-être que la solution serait de les mettre sur une clause where ?

Maintenant, comparez avec le pré-RFC lié ci-dessus. Ce serait introduire un _tag_ (au sens du système de types) directement dans la position des arguments de fonction, c'est-à-dire des liaisons variables pour le corps d'une fonction.

En d'autres termes : c'est très déroutant et, comme plusieurs personnes l'ont déclaré pour impl Trait en position arg, ce n'est pas nécessaire. S'il vous plaît, ne faites pas la même erreur deux fois pour ajouter une fonctionnalité qui n'en vaut pas la peine. Surtout si vous « prétendez que ce sont des arguments ». Rust irait dans la mauvaise direction ici. La syntaxe devrait être plus légère, oui, mais pas plus confuse. S'il te plaît, arrête.

@phaazon
Personnellement, je suis entièrement d'accord avec la proposition liée selon laquelle autoriser des constantes dans la position d'argument sera un très bon coup de pouce ergonomique. Il ne s'agit pas seulement de signatures de fonctions (on peut soutenir que les clauses where ne "résolvent" pas du tout le problème, mais rendront plus difficile la compréhension des signatures, car vous devez analyser 3 endroits au lieu d'un seul), mais aussi comment ces fonctions seront utilisées. Comparer:

let r = _mm_blend_ps::<{2 * C}>(a, b);
let r = _mm_blend_ps(a, b, 2 * C);

La deuxième ligne est à mon avis beaucoup plus naturelle que la deuxième. Un autre exemple serait la création d'une matrice : MatrixF32::new(3, 3) vs. MatrixF32::new::<3, 3>() . Je ne suis pas du tout d'accord pour dire que cette fonctionnalité sera "très déroutante". Pour l'utilisateur, ce n'est rien de plus qu'une exigence supplémentaire pour un argument de fonction. Nous pouvons déjà émuler les arguments const via des macros (voir les intrinsèques SIMD dans std ), mais c'est assez moche et inefficace.

Concernant impl Trait en position d'argument, au départ j'étais aussi contre cette fonctionnalité, mais après un certain temps j'ai changé d'avis et je pense que c'était une bonne décision au final. La seule partie incomplète pour le moment est l'interaction avec les paramètres de type explicites fournis via le turbofish. Je pense qu'une bonne solution serait de rendre les arguments impl Trait invisibles pour le turbofish, c'est-à-dire que les utilisateurs devraient l'utiliser lorsqu'ils sont sûrs qu'une désambiguïsation de type explicite ne sera pas nécessaire. Cela nous permettrait de réduire considérablement le besoin de _ intérieur de turbofish dans certains scénarios.

Je suppose qu'une discussion sur les arguments const est hors sujet ici, donc ne devrait probablement pas la continuer ici.

Une chose qui sépare les arguments const arguments impl Trait (ce que @PvdBerg1998 a mentionné) est qu'ils sont des arguments, avec tout ce que cela implique pour l'inférence de type.

Vous ne pouvez pas passer un type lui-même en tant qu'argument, mais vous pouvez passer une valeur, et laisser une fonction déduire ses arguments génériques const à partir de ses arguments normaux entre parenthèses est tout à fait raisonnable.

C'est assez courant en C++, par exemple :

template <typename T, size_t N>
size_t length(T (&array)[N]) {
    return N;
}

Ceci est subtilement différent de la simple acceptation d'un paramètre const et de sa désucrage en un paramètre générique const ( fn foo(const N: usize) -> fn foo<const N: usize>() ). C'est plus proche de contraindre un paramètre avec un paramètre générique const, puis d'inférer cet argument générique const à partir de l'argument. Ainsi, l'exemple de matrice ci-dessus pourrait ressembler à ceci :

impl<const W: usize, const H: usize> MatrixF32<W, H> {
    fn new(w: usize<W>, h: usize<H>) -> Self { .. }
}

...où usize<X> est une syntaxe inventée pour "un usize avec la valeur X ." (De même, vous voudrez peut-être usize<X..Y> pour les types à distance, qui ont été discutés dans le contexte de la généralisation de NonZero .)

Si vous voulez éviter de rogner dans les types à distance, vous pouvez regarder les langages à typage dépendant qui permettent aux paramètres d'être utilisés directement dans les types, avec un peu de réglage de la portée :

fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }

Ce ne sont génériques . Au niveau du système de types, cela signifie que leurs _valeurs_ sont actives au moment de la compilation, tandis que les valeurs d'un argument de fonction sont actives au moment de l'exécution. Mélanger les deux est extrêmement trompeur.

Comme je me soucie des systèmes de types, cette proposition va à l'encontre de nombreux principes sains et sains. Vous ne voulez pas mélanger les valeurs et les types. Parce que, oui, un N (notez que je n'ai pas dit usize , j'ai dit N ), même si vous pensez que c'est une valeur, s'apparente beaucoup plus à un type qu'une valeur. Comme justification, Haskell n'a pas de génériques const mais il a DataKinds , permettant de lever les constructeurs de données réguliers en tant que types :

foo :: Integer -> Integer
foo 0 -- 0 has type Integer

-- but
data P (a :: Integer)

type MyP = P 10 -- 10 has kind Integer, which “value” is the 10 type

Donc:

fn foo<const X: usize>()

Ici, X s'apparente plus à un type qu'à une valeur.

Aussi, je me soucie de la monomorphisation. Si je lis ceci :

let x = foo(12, "Hello, world!", None);

Avec votre proposition, si nous regardons la définition de foo , il est difficile de savoir quels arguments vont monorphiser foo . Parce que chaque fois que vous passez une valeur différente pour un générique const, vous créez une nouvelle fonction complète.

Peut-être que cela semble plus intuitif pour _vous_ et _vos raisons_, mais j'ai aussi des raisons de dire que ce n'est pas du tout intuitif en termes d'exactitude de type. Affirmer qu'il s'agit d'arguments revient à affirmer que les fonctions paramétrées, en mathématiques, ont leurs paramètres d'arguments. Vous confondez paramètres et arguments, démontrant à quel point cette idée est mauvaise.

Vous m'avez peut-être mal lu, j'ai spécifiquement dit " const arguments ... sont des arguments ", pas " const génériques". Mon premier exemple de Rust est également exactement équivalent à DataKinds ( usize<N> est la version au niveau du type de N ). Il n'y a pas de problème ici en termes d'exactitude de type - je ne discute pas en termes d'intuition mais par analogie avec des systèmes de type existants, établis et sains.

Quant à votre préoccupation concernant la monomorphisation, ce n'est pas différent d'aujourd'hui ! Chaque argument peut potentiellement provoquer une nouvelle monomorphisation. J'ajouterais également que la solution à ce problème est de réduire le coût de tous les génériques en partageant du code non dépendant entre les instances, y compris et surtout en convertissant les génériques const en valeurs d'exécution lorsque cela est possible.

(Et je suis complètement déconcerté par votre affirmation selon laquelle je confond les paramètres et les arguments, j'ai pris soin de les distinguer.)

Je comprends ce que tu veux dire mais je me sens toujours très concerné. Parce que l'_argument_ est étiqueté _const_, cela signifie que vous ne pouvez pas lui transmettre _une valeur_, lors de l'exécution. Vous devez lui transmettre une _valeur constante_, qui correspondra à la _const générique_. Soit dit en passant, vous ne l'avez pas mentionné ici mais c'est juste une application d'un _type unique_ si vous considérez 10 comme un type : sa valeur unique possible est 10 .

Comme j'ai été contre impl Trait en position arg, je suis contre celui-là aussi (et vous comprendrez pourquoi nous pouvons comparer ici). Plusieurs points :

  1. Je ne pense pas que ce soit nécessaire alors que _const generics_ ajoute beaucoup de valeurs.
  2. Puisqu'ils s'apparentent à des types, ils devraient appartenir à l'endroit où se trouvent les types, et non à l'endroit où se trouvent les variables.
  3. J'aimerais vraiment savoir pourquoi certaines personnes pensent que <…> est plus difficile que (…) , surtout si l'on considère struct vs fn . Actuellement, struct n'a que <…> et fn a les deux, car ils peuvent tous deux être paramétrés.
  4. Ce que je n'ai vraiment pas aimé avec impl Trait en position arg, c'est le fait qu'il est plus difficile de voir avec quoi une fonction est vraiment définie avec des paramètres. Lorsque vous lisez foo<A, B> , vous savez qu'il y a deux paramètres qui donneront lieu à une définition et à une implémentation de fonction.

Je sens ce que tu veux faire ici. foo(1, 3) se sent mieux pour vous que foo<3>(1) , mais pas pour moi. La raison en est que foo(1, 3) doit accepter le deuxième argument comme étant basé sur l'exécution et la proposition que vous donnez l'interdit. Je peux voir des applications, cependant, en particulier avec des tableaux, mais je n'aime pas à quoi cela ressemble actuellement. Je serais beaucoup plus pour une solution qui dit « si un argument est const , on peut en déduire un type variable/const générique.

fn foo<const N: usize>(a: usize, b: usize | N);

C'est juste une syntaxe imaginaire que j'ai trouvée pour expliquer mon point : si vous passez b comme valeur const, N = b et vous vous retrouvez avec votre belle syntaxe :

foo(1, 3)

Si b n'est pas une valeur const, vous devez passer N explicitement :

foo<3>(1, x)

Aussi, quelle serait la syntaxe pour cela, avec votre proposition :

fn foo<const N: usize>(x: [f32; N]);
fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }

Cela nous amène à nous interroger sur les éléments associés, en bref new() est une fonction associée, et doit être associée à un type concret, les éléments génériques const ont une sémantique comme "Ce type ayant ce paramètre...", c'est-à-dire de type dépendant. Ainsi, new() dans le contexte ne devrait pas avoir d'arguments. L'utilisation explicite devrait ressembler à Type<32>::new() . Et bien sûr, tous les arguments génériques const doivent être élidables.

Je sens ce que tu veux faire ici. foo(1, 3) se sent mieux pour vous que foo<3>(1), mais pas pour moi. La raison en est que foo(1, 3) devrait accepter le deuxième argument comme étant basé sur l'exécution et la proposition que vous donnez l'interdit.

Ce n'est pas ma motivation ni mon intention - je ne parle pas de ce qui "se sent mieux" et je ne pense pas que la distinction compilation/exécution doive être confondue avec la distinction type/niveau de valeur. Les deux sont déjà séparés - les éléments const (valeur ou fn) ne sont certainement pas des types !

Tout ce à quoi je veux en venir, c'est la connexion sémantique que l'inférence de type établit entre les paramètres de niveau de valeur et les paramètres de niveau de type. Il n'y a aucun rapport avec impl Trait - mon propos, encore une fois, était de distinguer les arguments const de impl Trait , précisément pour éviter ce genre d'angoisse improductive.

Aussi, quelle serait la syntaxe pour cela, avec votre proposition :

fn foo<const N: usize>(x: [f32; N]);

Cette syntaxe ne changerait pas ! Je ne propose pas de supprimer les paramètres const du niveau de type (syntaxiquement ou sémantiquement), mais de les ajouter au niveau de valeur, en partie pour faciliter l'inférence de ceux de niveau type.

Chez cppcon, il y a eu une discussion sur les paramètres de la fonction constexpr, qui semble similaire à ce dont parle

Cela semble être une bonne idée, mais cela devrait être mis au point une fois que nous aurons terminé l'implémentation initiale de const-generics.

CppCon 2019 : David Stone - Suppression de la métaprogrammation de C++, partie 1 de N : paramètres de fonction constexpr

Edit: Cela a été mentionné dans ce fil, voici le document connexe pour les paramètres de la fonction constexpr
https://github.com/davidstone/isocpp/blob/master/constexpr-parameters.md

Je voudrais demander que nous réservions la discussion d'un changement syntaxique aussi important soit pour un fil sur les internes, soit pour un problème (ou une modification des relations publiques) sur le référentiel RFC. La section commentaires de ce numéro est déjà assez longue et la syntaxe en question semble inutile pour un MVP utilisable de const génériques.

Si nous pouvions proposer des commentaires, nous le ferions probablement. Nous pourrions vouloir verrouiller le problème aux membres de l'équipe @rust-lang et masquer tous les commentaires hors sujet ?

Toutes les discussions de conception devraient avoir lieu sur le référentiel RFC et tous les rapports de bogues devraient être dans des numéros séparés.

cc @rust-lang/moderation Y a-t-il un précédent pour faire cela ?

Vous pouvez _convertir_ un commentaire en un problème, mais pas déplacer les commentaires. Le verrouillage est bien, et je cacherais juste les commentaires hors sujet.

Avec le début de la nouvelle année, j'ai pensé qu'il serait bon de donner une autre mise à jour sur où nous en sommes maintenant avec les génériques const. Cela fait un peu plus de 2 ans que le RFC des génériques const a été accepté. Au cours de la première année (environ 2018), une série d'importants efforts de refactorisation ont été entrepris pour faciliter les génériques const en améliorant la gestion des paramètres génériques généralement dans l'ensemble du compilateur. Au cours de la deuxième année (environ 2019), les travaux ont commencé sur la mise en œuvre des génériques const eux-mêmes : d'abord en faisant fonctionner le strict minimum, puis en ralentissant l'amélioration de l'intégrité de la fonctionnalité. Les gens ont également commencé à expérimenter l'utilisation de génériques const dans le compilateur lui-même. Des descriptions plus détaillées de ces efforts se trouvent dans les deux premiers messages de mise à jour : [1] , [2] .


Il y a eu de bons progrès au cours des deux derniers mois depuis la dernière mise à jour.

  • @maigre121 :

    • Correction d'un problème de rupture des génériques const sous la compilation incrémentielle et entre les caisses (https://github.com/rust-lang/rust/pull/65652)

    • Correction d'un bug avec l'inférence de type affectant les génériques const dans les diagnostics (https://github.com/rust-lang/rust/pull/65579)

    • Refactorisé l'interface pour l'évaluation const (https://github.com/rust-lang/rust/pull/66877)

  • @yodaldevoid a implémenté la désambiguïsation des paramètres type et const afin que les paramètres const n'aient plus besoin d'être enveloppés dans {} (https://github.com/rust-lang/rust/pull/66104)
  • @eddyb a corrigé l'utilisation des génériques const comme longueurs de tableau, une fois la normalisation paresseuse implémentée (https://github.com/rust-lang/rust/pull/66883)
  • @varkor :

    • Implémentation structural_match vérification

    • Correction de certains diagnostics pour prendre en compte les génériques const (https://github.com/rust-lang/rust/pull/65614)

    • Réalisation de diverses refactorisations et corrections d'inférences de type (https://github.com/rust-lang/rust/pull/65643, https://github.com/rust-lang/rust/pull/65660, https://github. com/rust-lang/rust/pull/65696)

Un grand merci à tous ceux qui ont aidé avec const Generics !


Et après? Le plus gros bloqueur pour les génériques const à l'heure actuelle est la normalisation paresseuse , qui est requise pour certains types de limites génériques const (comme dans les tableaux ). @ skinny121 étudie actuellement la normalisation paresseuse, poursuivant ses efforts fantastiques pour éliminer les gros bogues dans les génériques const. Cela résoudrait bon nombre des problèmes actuels avec les génériques const.

Mis à part la normalisation paresseuse, il existe encore un nombre important de bogues et de coupures de papier que nous aimerions résoudre. Si vous souhaitez résoudre l'un des problèmes restants, n'hésitez pas à me contacter à ce sujet, ou sur Discord ou Zulip pour obtenir des conseils sur la façon de commencer. Je suis convaincu que nous pouvons faire de bons progrès en 2020 et, espérons-le, approcher un point où la stabilisation deviendra une discussion viable !

Existe-t-il un sous-ensemble de génériques const qui n'atteint pas la normalisation paresseuse et qui n'a pas beaucoup de bogues et de coupures de papier, mais qui peut exprimer une bonne quantité de code utile ? Je remarque que les impls std de nombreux traits pour les tableaux semblent fonctionner correctement. Peut-être y a-t-il un rétrécissement qui permettrait à d'autres caisses d'écrire le type d'impls que nous avons dans std pour leurs propres traits, même s'ils ne prennent pas en charge toutes les fonctionnalités les plus sophistiquées ?

Nous sommes maintenant à plus de la moitié de l'année, je vais donc résumer brièvement le travail en cours. Il y a eu beaucoup de personnes impliquées dans l'amélioration du support des génériques const au cours des 6 derniers mois, alors merci à tous ceux qui ont aidé d'une manière ou d'une autre ! Je tiens particulièrement à remercier @skinny121 et @lcnr , qui ont tous deux abordé de grandes fonctionnalités qui manquaient aux génériques de const : la normalisation paresseuse des constantes et la prise en charge des arguments const dans les appels de méthode , ainsi que la correction de nombreux bogues difficiles. Leurs efforts sont évidents dans le résumé ci-dessous.

Les génériques de const sont maintenant utilisés à plusieurs endroits dans le compilateur, et il y a déjà des expérimentations avec des traits et des fonctions utilisant des génériques de const, par exemple slice::array_chunks et IntoIterator et FromIterator pour [T; N] (https://github.com/rust-lang/rust/pull/65819, https://github.com/rust-lang/rust/pull/69985). La restriction de longueur 32 pour les implémentations de traits de tableau est également en cours de préparation pour la suppression .

Nous discutons actuellement de la stabilisation d'un sous -


  • @maigre121 :

    • Implémentation de la première version de la normalisation paresseuse (https://github.com/rust-lang/rust/pull/68505, https://github.com/rust-lang/rust/pull/69181, https://github. com/rust-lang/rust/pull/67717, https://github.com/rust-lang/rust/pull/67890)

    • Performances améliorées (https://github.com/rust-lang/rust/pull/68118)

    • Correction de diverses erreurs (https://github.com/rust-lang/rust/pull/68143)

  • @lcnr :

    • Ajout de la prise en charge des génériques const explicites dans les arguments de type pour les méthodes (https://github.com/rust-lang/rust/pull/74113)

    • Terminé la première implémentation de la normalisation paresseuse pour les constantes (https://github.com/rust-lang/rust/pull/71973) avec @skinny121

    • Ajout d'une peluche pour unused_braces (https://github.com/rust-lang/rust/pull/70081)

    • Correction d'un problème avec les génériques const dans les modèles (https://github.com/rust-lang/rust/pull/70562)

    • Interdit dyn Trait pour les paramètres génériques const (https://github.com/rust-lang/rust/pull/71038)

    • Jolie impression améliorée (https://github.com/rust-lang/rust/pull/72052)

    • Correction de diverses erreurs (https://github.com/rust-lang/rust/pull/70032, https://github.com/rust-lang/rust/pull/70107, https://github.com/rust- lang/rust/pull/70223, https://github.com/rust-lang/rust/pull/70249, https://github.com/rust-lang/rust/pull/70284, https://github. com/rust-lang/rust/pull/70319, https://github.com/rust-lang/rust/pull/70541, https://github.com/rust-lang/rust/pull/72066, https : //github.com/rust-lang/rust/pull/74159)

    • Refactorisation (https://github.com/rust-lang/rust/pull/70478)

    • Tests ajoutés (https://github.com/rust-lang/rust/pull/74392, https://github.com/rust-lang/rust/pull/74445)

  • @eddyb :

    • Correction de problèmes avec les génériques const dans les longueurs de tableau (https://github.com/rust-lang/rust/pull/70452)

    • Jolie impression améliorée (https://github.com/rust-lang/rust/pull/71232)

    • Amélioration de la gestion des erreurs (https://github.com/rust-lang/rust/pull/71049)

    • Refactorisation (https://github.com/rust-lang/rust/pull/70164)

  • @Aaron1011 : erreur corrigée avec l'utilisation des génériques const cross-
  • @petrochenkov : correction de problèmes avec la résolution de noms et les génériques const (https://github.com/rust-lang/rust/pull/70006)
  • @yodaldevoid : correction d'un problème d'utilisation à vie dans les paramètres génériques const (https://github.com/rust-lang/rust/pull/74051)
  • @contrun : correction d'un ICE (https://github.com/rust-lang/rust/pull/69859)
  • @oli-obk : refactorisation (https://github.com/rust-lang/rust/pull/69981)
  • @Centril : diagnostic amélioré (https://github.com/rust-lang/rust/pull/70261)
  • @DutchGhost : ajout d'un test (https://github.com/rust-lang/rust/pull/70539)
  • @varkor :

    • Jolie impression améliorée (https://github.com/rust-lang/rust/pull/67909, https://github.com/rust-lang/rust/pull/68111)

    • Correction de diverses erreurs (https://github.com/rust-lang/rust/pull/67906, https://github.com/rust-lang/rust/pull/68388, https://github.com/rust- lang/rouille/tirer/68434)

    • Diagnostics améliorés (https://github.com/rust-lang/rust/pull/70845)

    • Tests ajoutés (https://github.com/rust-lang/rust/pull/68312, https://github.com/rust-lang/rust/pull/70939)


Alors que les génériques const ont déjà parcouru un long chemin, il y a toujours des bugs et des arêtes vives . Si vous souhaitez résoudre l'un des problèmes restants, n'hésitez pas à me contacter , @eddyb sur le problème, ou sur Zulip, pour obtenir des conseils sur la mise en route. Merci!

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