Rust: Tracking-Problem für const-Generika (RFC 2000)

Erstellt am 15. Sept. 2017  ·  202Kommentare  ·  Quelle: rust-lang/rust

Tracking-Problem für rust-lang/rfcs#2000

Aktualisierung:

Wenn Sie helfen möchten, werfen Sie einen Blick auf die offenen const-Generika-Probleme und wenden Sie sich an @varkor , @eddyb , @yodaldevoid , @oli-obk oder @lcnr, um Hilfe beim Einstieg zu erhalten!


Blockierende Stabilisierung:

  • [ ] Entwurf:

    • [ ] Auflösen der Reihenfolge von const- und type-Parametern mit Standardparametern

    • [ ] Entscheiden Sie, was die beste Balance zwischen UX/Implementierungskosten ist, um abstrakte const-Ausdrücke zu vereinheitlichen.

    • [ ] Wie wir die Wohlgeformtheit von const-Ausdrücken bestimmen.

  • [x] Implementierung
  • [ ] Dokumentation

    • [ ] Rustikale Anleitung


Verbleibende Umsetzungsprobleme:

  • [ ] Lösen Sie verschiedene FIXME(const_generics) Kommentare auf.
  • [ ] Beheben Sie Bedenken hinsichtlich der Kanonisierung / Lazy-Normalisierung.
  • [ ] Untersuchen Sie den Umgang mit konstanten Parametern in Mustern.
  • [ ] Weitere Tests hinzufügen.
  • [ ] Implementieren Sie Standardwerte für const-Parameter ( FIXME(const_generics:defaults) ).
  • [ ] Andere A-const-Generika-Probleme beheben .
  • [ ] Audit-Verwendungen von has_infer_types .
  • [x] Verbieten Sie komplexe Ausdrücke für const-Argumente mit Parametern (vorerst), zB {X * 2} .
  • [ ] Audit-Diagnose (zB 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

Hilfreichster Kommentar

Hier ist eine Zusammenfassung der bisherigen Fortschritte bei const-Generika.


Bevor die Arbeit an const-Generika richtig beginnen konnte, mussten einige Refactorings durchgeführt werden. @jplatte hat die Aufgabe mit https://github.com/rust-lang/rust/pull/45930 übernommen. @jplatte begann dann

@yodaldevoid und ich haben dort weitergemacht, wo @jplatte aufgehört hat, stellten aber schnell fest, dass der Fortschritt durch die Art und Weise, wie generische Parameter im Allgemeinen gehandhabt wurden, behindert wurde. So begann eine Reihe von Änderungen, um die Handhabung von Generika in der gesamten Codebasis zu überarbeiten: 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.

Damit könnte die Implementierung von const-Generika ernsthaft beginnen. Seitdem haben @yodaldevoid und ich langsam aber sicher schrittweise Unterstützung für const-Generika hinzugefügt: 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 und die meisten vor kurzem https://github.com/rust-lang/rust/pull/59008. (Diese wurden größtenteils aus der Haupt-Pull-Anfrage von


Wie ist der Stand jetzt? Einige konst Generika Tests jetzt 🎉However arbeiten, gibt es noch diejenigen , die dies nicht tun, und es gibt eine Reihe von FIXME s über den gesamten Code , dass wir noch arbeiten müssen. Wir nähern uns jetzt jedoch näher und wir sind an einem Punkt angelangt, an dem es einige niedrig hängende Früchte gibt, wenn Sie helfen möchten.

  • Zuerst brauchen wir mehr Tests. Bisher gibt es nur eine Handvoll Const-Generika-Tests , aber wir hätten gerne noch viele mehr. Da uns eine Reihe von Problemen bekannt sind, brauchen wir im Moment keine Fehlerberichte, aber wir brauchen das Bestehen von Tests. Wenn Sie einen Testfall haben, der funktioniert (und einem vorhandenen Test nicht allzu ähnlich sieht), können Sie einen Pull-Request öffnen, um ihn hinzuzufügen.
  • Zweitens gibt es, wie bereits erwähnt, eine Reihe von FIXME(const_generics) die über den Code verstreut sind. Wir planen, uns durch sie zu arbeiten, aber @yodaldevoid und ich haben nur so viel Zeit. t doppelter Aufwand).

Ich habe im oberen Post einen Überblick über einige der verbleibenden Implementierungsprobleme geschrieben, bevor const generics für die richtigen Tests bereit ist, um eine grobe Vorstellung davon zu geben, was noch zu tun ist.

Es hat seine Zeit gedauert, aber wir machen stetig Fortschritte und hoffen, das Tempo mithalten zu können. Es ist motivierend, endlich zu sehen, wie sich die Dinge fügen!

Alle 202 Kommentare

44275 hat ein ConstEvaluatable Prädikat hinzugefügt, und WF([T; expr]) erfordert jetzt ConstEvaluatable(expr) , da expr träge ausgewertet wird (auch wenn es nur ein ganzzahliges Literal ist). Um dieses Prädikat zu erfüllen, muss der Ausdruck erfolgreich ausgewertet werden, während die Normalisierung den Fehler ignoriert und den gefundenen Unevaluated Ausdruck einfach unberührt lässt, was mehr oder weniger bei zugeordneten Typprojektionen der Fall ist. Ich erwarte, dass das gleiche System auf Const-Generika skaliert.

@EpicatSupercell hat Interesse bekundet, daran zu arbeiten, ich werde sie bei der ersten Implementierung begleiten. Wir können jedoch aufgrund der in #44275 beschriebenen Einschränkungen nicht zu weit gehen.

Das heißt, wir benötigen die faule Normalisierung von @nikomatsakis , damit in Typen eingebettete konstante Ausdrücke die Grenzen des Gültigkeitsbereichs (von der Funktion / Typdefinition / Impl / etc., in der sie sich befinden) einhalten können, ohne die Hälfte der Zeit zyklische Abhängigkeiten zu erzeugen.

Implementierungswegpunkte (für ein direkteres Mentoring suchen Sie @eddyb auf Gitter oder eddyb auf IRC):

  • Deklaration / Syntax:
  • Deklaration / Semantik

    • Datenstrukturen: ty::Generics - const Parameter neben Typ-Parametern hinzufügen

    • Konvertierung von HIR: generics_of - Erstellen Sie ty::ConstParameterDef aus hir::ConstParam

  • Verwendung / Namensauflösung

    • Datenstrukturen: Def - eine Variante für const Parameter hinzufügen

    • Namensauflösungspass: with_type_parameter_rib - unterstützt sowohl Typ- als auch const Generika

  • Verwendung / Semantik

    • ConstVal - Param Variante hinzufügen ähnlich ty::TyParam

    • Konvertierung von HIR: TyArray mit einer Länge von ExprPath , die in Def::ConstParam sollte ConstVal::Param anstelle von ConstVal::Unevaluated - in ähnlicher Weise wie aus Def::TyParam ty::TyParam

    • subst::Kind - Unterstützen Sie &ty::Const und überprüfen Sie as_const auch dort, wo as_type und as_region überprüft werden

  • Inferenz

Beachten Sie, dass all dies impl<T, const N: usize> Trait for [T; N] {...} zulassen sollte, aber keinen konstanten Ausdruck an einen Typ/eine Funktion übergeben sollte, zB ArrayVec<T, 3> .

Ich würde das gerne mal ausprobieren :)

@jplatte @eddyb Gibt es dazu Neuigkeiten?

@samsartor gab es ein Refactoring, das vor den Hauptimplementierungsarbeiten durchgeführt werden musste. Das ist jetzt fast geschafft (ich warte derzeit auf Feedback). Ich weiß nicht genau, wie viel Arbeit es danach gibt. Mit dem Parsen von const params habe ich anfangs vor dem Refactoring begonnen. Es ist noch nicht fertig, aber ich sollte in der Lage sein, das relativ bald zu erledigen.

@jplatte Es wäre schön, wenn du den Pull-Request verlinken könntest. Ich konnte es nicht finden.

Es gibt noch keine PR. Alle meine Arbeiten finden Sie hier .

Bisher gute Arbeit

Es gibt jetzt eine PR für die Vorarbeiten ( Generics Refactoring): #45930

Ich denke, dies ist bereits enthalten, aber es wäre gut, die Faltung im C++-Stil zu handhaben, um n-dimensionale Arrays im linearen Algebra-Stil mit unterschiedlichen Längen zu handhaben, z. B. ein 4x4 [[f64;4];4]oder ein 4x3x5x6-dimensionales Array [[[[ f64;6];5];3];4] und in der Lage sein, spezialisierte Methoden dafür UND richtige Merkmalsimplementierungen für Skalare richtig dimensionierte Vektoren usw. richtig zu umhüllen und zu generieren. Siehe https://gist.github.com/huhlig /8b21850b54a75254be4b093551f8c2cb für ein rudimentäres Beispiel.

Ich erinnere mich nicht, dass jemand Fold-Ausdrücke für Rust vorgeschlagen hat, geschweige denn als Teil dieses RFC. Aber da es sich um Rust handelt, gibt es einen Grund, warum wir Fold-Ausdrücke nicht als normales Makro statt als neue dedizierte Syntax implementieren konnten?

Was sind die nächsten Schritte nach der Zusammenlegung von #45930? @jplatte

@kjaleshire Der nächste Schritt besteht darin, das Parsen von const-Generika zu implementieren (siehe Kommentar von @eddyb weiter oben). Ich habe bereits damit begonnen, bevor klar wurde, dass das Refactoring im Refactoring notwendig wäre. Meine bestehenden Arbeiten dazu finden Sie hier ; es wurde jedoch noch nicht rebasiert, seit die Refactoring-PR zusammengeführt wurde.

@jplatte Ich glaube, du wolltest @kjetilkjeka erwähnen.

Danke für das Update! Ich bin sicher, dass ich bei weitem nicht der einzige bin, der sich auf dieses Feature freut. Mach weiter so!

@jplatte möchte nicht ungeduldig sein, aber wurde daran gearbeitet? Brauchst du Hilfe? Soll jemand helfen?

@est31 Entschuldigung. Ich hatte schon eine Weile keine Zeit mehr, daran zu arbeiten, und ich bin mir nicht ganz sicher, wann ich die Zeit haben werde. Vielleicht ist es am besten, wenn jemand anderes dort weitermacht, wo ich aufgehört habe. Ich denke, der Parsing-Teil ist größtenteils erledigt. Es gibt zwei Stellen im Code, an denen ich nicht sicher war, was zu tun ist, wenn ein generischer Parameter ein const-Parameter ist:

Es gibt auch noch keine Tests für das Parsen von const-Generika (und für den hübschen Druckcode, den ich gleichzeitig aktualisiert habe). Ich bin mir nicht sicher, welche anderen Informationen ich bereitstellen kann, die benötigt / hilfreich wären, damit jemand anderes weiter daran arbeiten kann, aber Sie können mich gerne anpingen, wenn etwas an meinem Code unklar ist.

@jplatte ~ None im ersten und nichts im zweiten~~ (cc @pnkfelix @nikomatsakis)

EDIT : In beiden Fällen nichts zu tun.

@eddyb Sind Sie sicher, dass für das erste verlinkte Snippet None werden soll? Ich verstehe vielleicht nicht, was hier vor sich geht, aber mir scheint, dass nichts getan werden sollte. Wenn None zurückgegeben wird, werden andere Parameter, die möglicherweise unsicher sind, übersprungen.

@yodaldevoid Entschuldigung, du hast Recht, ich wusste nicht, dass dies auf Generics passiert .

Ich würde gerne dort weitermachen , wo @eddyb durch, da ich Zeit hatte, nur zu sehen, ob ich etwas erreichen könnte. Ich bin an dieser Stelle zum Abschnitt "Use / Semantik" gekommen. Ich werde wahrscheinlich früh genug bei einem der Chats vorbeischauen, um Fragen zu stellen.

@yodaldevoid Toll zu hören. Ich weiß nichts über Gitter, aber Sie werden auf jeden Fall viele Anleitungen zu #rustc und/oder #rust-internals im IRC erhalten!

@yodaldevoid : bereit für eine Zusammenarbeit? Ich habe auch einige Nachforschungen zu diesem Thema angestellt (😅) – vielleicht könnten wir die Lücken in der Arbeit des anderen schließen. (Sie können hier sehen, was ich bisher gemacht habe.) Vielleicht könnten wir uns im IRC (#rustc oder #rust-internals) darüber unterhalten?

@varkor Es sieht so aus, als ob du weiter bist als ich. Ich wäre auf jeden Fall bereit mitzuarbeiten. Ich werde versuchen, dich irgendwann im IRC zu schnappen und in der Zwischenzeit zu sehen, ob ich etwas getan habe, was du noch nicht getan hast.

Gibt es diesbezüglich Fortschritte?
Ich schreibe Code für Embedded, und ich brauche wirklich Const-Generika.

@qwerty19106
Hier werden, wenn auch langsam, Fortschritte gemacht. @varkor und ich haben ab und zu daran gearbeitet, wie wir Zeit hatten. Ich habe diese Woche einige Fortschritte gemacht und wir sehen das Licht am Ende des Tunnels für die grundlegende Nutzung.

Über die bloße Implementierung von const-Generika hinaus haben wir (varkor) einige Bereinigungen vorgenommen, um dies alles möglich zu machen (siehe #48149 und #48523). Ich glaube, der aktuelle Plan besteht darin, zu warten, bis diese beiden Pull-Requests abgeschlossen sind, bevor Const-Generika eingezogen werden, aber Varkor kann mehr dazu sagen.

Ich verstehe Ihren Bedarf an const-Generika für eingebettete Arbeit wirklich. Ich habe damit angefangen, weil ich auch wirklich Const-Generika möchte, damit ich große Teile des eingebetteten Codes aufräumen und typsicher machen kann.

TL;DR: Es geht voran, aber das ist komplex. Ich fühle dich auf der eingebetteten Front.

Für das Kontrollkästchen "Dokumentation" wäre es toll, etwas in der rustc-Anleitung zu finden .

Danke @yodaldevoid für deine Antwort. Ich freue mich auf das Ende Ihrer Arbeit.

Update für Neugierige (da war ich auch neugierig). Betreff: die beiden oben genannten PRs: #48523 hat sich zusammengeschlossen und #48149 macht stetig Fortschritte.

@mark-im Gutes Zeug! Schöne Arbeit von @varkor. Wann ist ungefähr die ETA, weißt du? :-)

Hallo, @flip111.

Sieht so aus, als ob die zweite große Refactoring-PR #48149 zusammengeführt wurde :)

/cc ich

Weiter @varkor PR #51880

Ich wollte nur ein kurzes Update geben, da ich weiß, dass einige Leute nach dem Fortschritt bei const-Generika gefragt haben.

Um etwas Kontext zu geben, hatten @yodaldevoid und ich im März eine erste Implementierung, die fast funktionierte (der Großteil der Implementierung schien abgeschlossen zu sein und wir waren gerade dabei, einige verbleibende Abstürze zu beseitigen). Der Zweig, an dem wir arbeiteten, war jedoch Pre-Miri. Als miri zusammengeführt wurde (und in einer Reihe von nachfolgenden PRs), änderte sich der Code für den Umgang mit ständiger Bewertung erheblich, was bedeutete, dass vieles von dem, was wir gemacht hatten, veraltet war.

Darüber hinaus wurde entschieden, dass der generische Parametercode im Allgemeinen bereinigt werden muss, bevor Const-Generika angehängt werden, sowohl um die Lesbarkeit zu verbessern, als auch um Fehler zu machen, bei denen wir in einem bestimmten Fall vergessen haben, mit const-Generika umzugehen machen. Dies wurde in https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/48149 durchgeführt und wird in https://github abgeschlossen. com/rust-lang/rust/pull/51880. Diese waren etwas komplizierter, als ich ursprünglich erwartet hatte, und es dauerte etwas länger, als erwartet.

In der Zwischenzeit haben @yodaldevoid und ich daran gearbeitet, unsere ursprüngliche Implementierung mit allen nachfolgenden Änderungen in rustc kompatibel zu machen. Es hat eine Weile gedauert, aber wir kommen dahin (obwohl es das ewige Problem gibt, nie so viel Zeit zu haben, wie Sie erwartet hatten). Ich hoffe, dass wir in dieser Hinsicht bald gute Nachrichten haben. (Inzwischen macht https://github.com/rust-lang-nursery/chalk gute Fortschritte, was einige der ursprünglich beschriebenen Schwierigkeiten von @eddyb beheben sollte .)


Einige Leute haben gefragt, wie sie helfen können: Ich denke, zu diesem Zeitpunkt wird es für uns einfacher sein, die anfängliche Implementierung abzuschließen und dann zu sehen, welche Teile Aufmerksamkeit erfordern. Sobald das fertig ist, werden wir viele Tests brauchen, die const-Generika an verschiedenen Stellen verwenden (einschließlich ungültiger, für Fehlermeldungen), also könnten wir das definitiv mit viel Hilfe tun! Wir informieren Sie, wenn das passiert!

Tut mir leid, wenn dies nicht der richtige Ort dafür ist, aber ich habe einen Vorschlag zur Gleichheit abstrakter const-Ausdrücke. Im Allgemeinen reduziert es sich direkt auf volle abhängige Typisierung und unentscheidbares Territorium. In Rust wird jedoch schließlich alles mit konkreten Werten/Typen instanziiert, sodass wir einige Gleichheiten geltend machen und deren Überprüfung auf monomorphe Instanzen verschieben können.

Was ich meine ist, dass eine relativ einfache Möglichkeit, die Gleichheit abstrakter const-Ausdrücke zu überprüfen, die folgende ist:

  • automatisch mit syntaktischer Gleichheit umgehen (dh N+1 == N+1 sollte sofort funktionieren)
  • erlauben Sie dem Benutzer, zur Definitionszeit Gleichungen wie N+M == M+N hinzuzufügen (vielleicht in der where Klausel?). Diese Gleichungen können von der Gleichheitsprüfung (unter Verwendung einer Form von Kongruenzabschluss) verwendet werden. Eine Definition, die keine Prüfung mit diesen bereitgestellten Gleichungen durchführt, wird abgelehnt.
  • an einem monomorphen Expansionspunkt können alle Gleichungen überprüft werden, indem man die const exprs tatsächlich berechnet, die nicht mehr abstrakt sind. Hier gibt es eine Designentscheidung: Wenn eine Gleichung auf false reduziert wird, könnte entweder ein Kompilierungsfehler vorliegen ("Gleichungen sind Axiome") oder das Merkmal kann nicht instanziiert werden ("Gleichungen sind Einschränkungen")
  • an einem parametrisierten Expansionspunkt werden diese Gleichungen übertragen: Wenn Sie eine Funktion haben, die f mit N f parametrisiert ist, wobei N+0==N , darf diese Gleichung nicht in einem Aufrufer g da es an jeder monomorphen Stelle überprüft werden muss, an der g aufgerufen wird.

Der Vorteil dieser Methode besteht darin, dass in Rustc selbst kein Theorembeweiser, SMT-Löser oder arithmetischer Umschreiber erforderlich ist. "Nur" eine syntaktische Gleichheitsprüfung von Typen, Modulo-Aliasnamen und Modulo ein vom Benutzer bereitgestellter Satz von Gleichungen.
Der Nachteil ist, dass es für den Benutzer manueller ist, da scheinbar offensichtliche Gleichheiten wie M+N=N+M explizit hinzugefügt werden müssen.

Beispiel:

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

Gleichungen als Axiome

Das bedeutet, dass e1 == e2 immer wahr sein sollte, also ist es nicht wirklich eine where Einschränkung, sondern eher eine assert_eq!(e1,e2) , die von der Typprüfung verwendet werden kann. Hier verspricht der Implementierer, dass dies immer der Fall ist, und setzt seine Benutzer Kompilierungsfehlern aus, wenn sie Parameter bereitstellen, die die Gleichung widerlegen.

Gleichungen als Nebenbedingungen

Hier ist eine Klausel where e1 == e2 eine Bedingung, die für die erfolgreiche Verwendung dieses parametrisierten Merkmals/dieser parametrisierten Funktion erfüllt sein muss. Das bedeutet, dass e1 == e2 nicht immer gelten muss, was für Gleichungen interessant sein kann, die nur über eine Domäne wahr sind, wie etwa (x*y) / z == (y*x) / z die für z==0 nicht instanziiert werden können.

@c-cube Wir haben bereits ein System für "implizite where Klauseln", zumindest für Funktionen, abgeleitet von " WF (wohlgeformtheits-)Regeln", dh wenn Sie eine fn foo<'a, 'b>(x: &'a &'b i32) {} dann müssen Anrufer 'b: 'a erfüllen (als Ergebnis von WF(&'a &'b i32) -> &'b i32: 'a -> 'b: 'a ).

Wir können dieses System wiederverwenden (tatsächlich ist dies bereits implementiert), um durch die Signatur gegebene Einschränkungen (der Form "dieser konstanter Ausdruck wird erfolgreich ausgewertet") an die Aufrufer zu übertragen, während alles, was nur innerhalb des Rumpfes verwendet wird, immer noch where Klauseln.

Das meiste andere, was Sie beschreiben, scheint dem Geplanten nahe zu kommen (einschließlich einer Form von "syntaktischer Gleichheit"), aber wir wollen keine "Monomorphisierungszeit"-Überprüfungen, wir können stattdessen Wege haben (entweder implizit oder durch where Klauseln), um "Constraints an Instanziatoren weiterzugeben", und dann erzeugen wir Fehler, bei denen Einschränkungen "noch zu generisch" sind (also unbekannt, wenn sie gelten), oder sonst, wenn wir sie auswerten können und sie zufällig nicht gelten.

Alles andere, was du beschreibst, scheint dem, was geplant ist, nahe zu kommen

@eddyb kennen Sie Referenzbeiträge, in denen diese Pläne diskutiert werden?

@flip111 Wahrscheinlich einige der RFCs. Vielleicht weiß @Withoutboats es besser.

# 51880 ist fertig: tada:: tada:

@withoutboats @oli-obk Was halten Sie davon, die richtige Vereinheitlichung von Ausdrücken wie N + 1 (von zB [T; N + 1] ) zu blockieren, um eine grundlegende Form der symbolischen Auswertung in miri ?

Ich denke, wir hätten bereits einen Mechanismus, um es zu codieren. Wenn @varkor ConstValue::{Infer,Param} hinzufügt, können wir damit "(flach) gültige, aber unbekannte" (symbolische) Werte verfolgen und dann auch Werte einbeziehen (hauptsächlich Ganzzahlen) Operationen und Aufrufe darüber.

Alle Nicht- assert Kontrollfluss-Entscheidungen würden immer noch bekannte Werte erfordern, aber assert selbst könnte vielleicht als AssertThen(condition, assert message, success value) .
(Sowohl condition als auch success value könnten symbolisch sein!)

Die Möglichkeit, die symbolischen Werte in ty::Const zu exportieren, bedeutet, dass wir einige der Vereinigungen außerhalb von miri implementieren können und (die meisten?) Operationen als undurchsichtig behandeln.

Wir müssen aufpassen, dass wir nichts von Inferenzvariablen annehmen, aber für zB N + 1 denke ich, dass wir die Vereinigung von zwei Add(Param(N), Bits(1)) .

@varkor Ein Testfall sollte meiner Meinung nach nur mit fauler Normalisierung funktionieren, ohne Vereinheitlichung:

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

Das würde nur funktionieren, weil:

  • N - 1 wird auf der Typebene genau einmal angezeigt

    • alles kann sich auf diesen Ausdruck beziehen, undurchsichtig

    • ( Mögliche Problemumgehung: type ArrayWithoutLast<T, const N: usize> = [T; N - 1]; )

  • es steht an einer Argumentposition innerhalb der Signatur

    • (Kann mich nicht erinnern, ob die Rückkehrposition auch funktionieren würde. @nikomatsakis?)

    • Anrufer können beweisen, dass es sich um WF handelt, indem sie konkrete Werte für N

    • "Aufsprudeln" der WF-Anforderung muss vereinheitlicht werden / ArrayWithoutLast

    • andere Verwendungen müssten "in eine where Klausel eingebettet werden", zB [T; N - 1]: Sized



      • kann sogar where [T; N - 1]: missbrauchen (wird das heute ignoriert? Autsch!)


      • Umgehe erneut die Vereinigung mit where ArrayWithoutLast<T, N>: ...



Insgesamt können wir uns also wahrscheinlich auf WF-Regeln verlassen, um den Benutzern die "Validierung" von const-Ausdrücken zu erzwingen, aber wir möchten immer noch eine Vereinheitlichung für andere Dinge (einschließlich der Vermeidung von Hacks wie ArrayWithoutLast ).

53645

Kleine Frage, die aus der Diskussion über Maßeinheiten stammt. Was sollten wir tun, wenn der Typ generisch über konstant nicht direkt davon abhängt? Zum Beispiel:

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

Ich nehme an, Sie meinten [(); N] , aber das funktioniert nur für usize .

Ich denke, es wäre sinnvoll, ein spezielles marker::PhantomConst das jeden const Typ erlaubt.

Hmm… Beim erneuten Lesen des RFC mit den neuesten Kommentaren frage ich mich: wäre so etwas erlaubt?

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

Ich kann mir nirgendwo ein Verbot vorstellen, hätte aber gedacht, dass es zumindest ein Beispiel verdient.

@Ekleog : Soweit mir bekannt ist, hängen const-Parametertypen anfangs möglicherweise nicht von Typparametern ab (obwohl dies für eine Erweiterung definitiv sinnvoll wäre). (Die Implementierung ist schwieriger, wenn wir dies zulassen, daher ist es sinnvoll, mit der einfachsten Version zu beginnen.)

Wie ist der Fortschritt dabei? Kennen wir eine ungefähre Zeit, zu der dies jede Nacht eintreten sollte?

@Zauberklavier Sobald dieser Pull-Request erledigt ist. Um daraus zu zitieren:

Es ist ein langer Weg

@newpavlov Ich ging davon aus, dass konstante Parameter zulässig wären, ohne in Feldern verwendet zu werden.
Der Grund, warum Typparameter verwendet werden müssen, ist die Varianz, aber Konstanten haben dieses Problem nicht.

@eddyb daran erinnere ich mich auch aus der RFC-Diskussion.

@varkor Das bedeutet also, dass PhantomConst das von @cuviper vorgeschlagen wurde, im aktuellen Status dieses RFC nicht existieren kann ... richtig? obwohl die neuesten Kommentare darauf hindeuten, dass PhantomConst sowieso nicht erforderlich ist.

Beeinflussen const-Parameter die Varianz der Typparameter, die sie beinhalten?

Derzeit glaube ich, dass const-Generika keine Typparameter in ihrem Typ haben können.

Mir ist nicht klar, was die erste funktionierende Version davon kann.

Zum Beispiel, ob der Code in diesem Kommentar kompilieren würde:
https://github.com/rust-lang/rfcs/pull/2581#discussion_r230043717

Wenn dieser Code kompiliert würde, wäre dies eine Möglichkeit, Einschränkungen für Konstanten zu erzwingen, bevor eine Syntax dafür erhalten wird.

@rodrimati1992 Sie können wahrscheinlich einfach (): IsTrue<{N < 128}> ohne einen separaten Typ ausführen .
Ich nehme an, Sie möchten die Einschränkung wiederverwenden, damit sie tatsächlich funktioniert? (weil das Kopieren des N < 128 Ausdrucks zunächst nicht funktioniert)
Du könntest trait Lt128<const N: usize> = IsTrue<{N < 128}>; , denke ich.
Es gibt auch die Möglichkeit, einfach where [(); 128 - N], schreiben, denke ich (aber ich bin mir nicht sicher, ob das in Panik gerät).

@rodrimati1992 Sie können wahrscheinlich einfach (): IsTrue<{N < 128}> ohne einen separaten Typ ausführen .
Ich nehme an, Sie möchten die Einschränkung wiederverwenden, damit sie tatsächlich funktioniert? (weil das Kopieren des N < 128 Ausdrucks zunächst nicht funktioniert)
Du könntest trait Lt128<const N: usize> = IsTrue<{N < 128}>; , denke ich.
Es gibt auch die Möglichkeit, einfach where [(); 128 - N], schreiben, denke ich (aber ich bin mir nicht sicher, ob das in Panik gerät).

Also, mit Merkmalsaliasen könnte ich das so umschreiben?:

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

Die Idee mit dem AssertTrait verwendet einen Typfehler, um eine in einen Typ eingebettete Fehlermeldung zu drucken, was im Fall von AssertLessThan128 ist:

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

von dem ich erwarte, dass es hilfreicher ist als where [(); 128 - N], , da es Ihnen in der Fehlermeldung sagt, warum der Kompilierzeitfehler aufgetreten ist.

Du kannst auch if !(N < 128) { panic!("...") } in einer Konstante machen, denke ich?

Ich dachte, die std::fmt-Architektur wird nicht da sein, bis die const trait Einschränkungen (oder etwas Ähnliches) eintreffen?

Mit diesem Ding können Sie Fehlermeldungen mit mehreren Werten haben (in Typen eingebettet).

Vielleicht ist es besser, darauf zu warten, dass const-Generika darüber sprechen, da es einfacher ist, ausführbare Beispiele zu zeigen.

@rodrimati1992 siehe https://github.com/rust-lang/rfcs/blob/master/text/2345-const-panic.md , es wird einen Sonderfall geben, um const panic!() bevor es so wäre über andere Funktionen möglich sein (vermutlich wird es zunächst keine benutzerdefinierte Formatierung von Werten ermöglichen).

@rodrimati1992 Ahh, ich

Wie ist der Stand davon?

Der nächste Schritt ist immer noch https://github.com/rust-lang/rust/pull/53645 AFAIK.

Das ist richtig. Um ein schnelles Update für diejenigen zu geben, die die PR #53645 nicht befolgen, haben const generics mindestens einen einfachen Anwendungsfall kompiliert und arbeiten. Was bleibt, ist die Fertigstellung von Codegen für andere Anwendungsfälle, einschließlich const generics in arrys, und einige Bereinigungen der Fehlerausgabe. Danach sollte die PR fertig sein und die Leute können damit spielen.

Könnte nicht zum Thema gehören, aber wird dies einer Variante oder Abzweigung von Chunks und verwandten Methoden ermöglichen, dass Item ein Array mit fester Größe während der Kompilierung anstelle eines Slice ist? Dies würde es einer for Schleife ermöglichen, ein unwiderlegbares Muster zu destrukturieren/zu binden, das die Elemente im Chunk auf Variablen wie for (first, second) in arr.chunks(2) abbildet, was derzeit fehlschlägt, und ich vermute (ohne Begründung) zugegeben), dass es in bestimmten Anwendungsfällen mehr Optimierung ermöglichen würde.

@jeffvandyke Sie denken vielleicht speziell an

@jeffvandyke Sie denken vielleicht speziell an

Ich warte nur darauf, dass dies implementiert und stabilisiert wird, bevor ich einen PR vorschlage, der eine solche API zusätzlich zu ChunksExact und seinen Varianten hinzufügt :)

Die Laufzeit- und Kompilierzeit-Varianten haben beide ihre Anwendungsfälle, Sie kennen Ihre Chunk-Größe nicht immer im Voraus. Optimierungstechnisch, wenn Sie ChunksExact mit einer Konstanten verwenden, sollte es nach meinen Tests mehr oder weniger gleich sein. Der Compiler kann alle Grenzüberprüfungen weg optimieren.

umgesetzt und stabilisiert werden

Ich würde vorschlagen, nicht auf die Stabilisierung zu warten, da eine solche API eine weitere gute Verwendung wäre, um diese Funktion zur Stabilisierung auszuüben.

Ich vermute, dass dies für impl Blöcke noch nicht funktioniert? Ich habe es versucht

#![feature(const_generics)]

struct The<const Val: u64>();

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

aber, wie in der Tat gewarnt, stürzte der Compiler mit

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

Der Fehler hängt mit const in impl<const Val: u64> da das Entfernen dieses Teils andere Fehler verursacht, aber nicht abstürzt.

aber lobt Rust dafür, dass es diese Funktion überhaupt in Betracht zieht. Ich hatte keine Ahnung, ob es funktionieren würde, aber die Syntax schien natürlich, ich habe mich dafür entschieden und lo Rustc sagte, dass es existiert :)

Ich bin nicht überrascht, dass es nicht funktioniert, da dieses mit Spannung erwartete Feature immer noch durch den Compiler gelotet wird (siehe zB #59008 und #58581 und die frühere Arbeit an #53645, die aufgegeben wurde, weil die PR zu groß war .) , aber immer noch als Tracker offen gehalten, um den Fortschritt anzukündigen).

Ich bin mir jedoch nicht sicher, ob von den aktuellen Implementierungsstubs Zugriffe auf Slices außerhalb der Grenzen zu erwarten sind. @varkor @yodaldevoid , kannst du mal nachsehen?

Ja, die Warnung ist richtig: const-Generika sind noch in keiner Form funktionsfähig. Es gibt noch ein paar weitere Pull-Requests, bevor sie bereit sind, damit herumzuspielen.

Tut mir leid, wenn dies nicht der richtige Ort ist, um Fragen zu stellen, aber ich habe nirgendwo besseres gefunden. Nur 2 Fragen:

  1. Kann eine einzelne Funktion bedingt const sein? Zum Beispiel könnte eine Funktion 2 Signaturen haben wie:

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

    In diesem Fall ist foo konstant, wenn A: const T ; Wenn A nicht const T implementiert, erfüllt es immer noch die Grenze, aber foo ist nicht mehr const. Es ist auch wichtig, dass der Autor in der Lage ist, für komplexere Beispiele eine beliebige generische Grenze anzugeben (z. B. where A::Output : Bar ). Ein gutes Beispiel dafür ist sogar einfache Arithmetik:

    // 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?
    

    Ich bin der festen Überzeugung, dass es definitiv eine Möglichkeit geben sollte, dies zu tun, und ich bin überrascht, dass dies im RFC nicht erwähnt wird (es sei denn, ich habe es übersehen?).

  2. _[weniger wichtig]:_ Wird es eine Möglichkeit geben, im Hauptteil einer const-Funktion zu erkennen, ob wir zur Kompilierzeit oder zur Laufzeit laufen? Ich denke, dass ein Makro, das cfg!() ähnelt, eine nützliche Erweiterung wäre, wenn auch aus keinem anderen Grund als dem Debuggen. cfg!() wird derzeit bereits zur Kompilierzeit ausgewertet, daher denke ich (/schätze), dass es in der Lage sein sollte, zu wissen, ob die Funktion als const oder als reguläre Funktion verwendet wird, da auch dies beim Kompilieren bestimmt wird -Zeit. Dies ist jedoch weniger wichtig als meine erste Frage.

@Coder-256

  1. Ja, siehe https://github.com/rust-lang/rfcs/pull/2632.
  2. Ich bin mir nicht sicher, ob das überhaupt möglich sein sollte, obwohl ich aufgrund des oben Gesagten auch nicht sicher bin, ob es notwendig ist.

@Coder-256 Diese beiden Fragen beziehen sich nicht auf const-Generika, sondern auf const-Funktionen. Const-Generika dienen dazu, generisch gegenüber consts zu sein (zB foo<2>() ), anstatt dass Funktionen zur Kompilierzeit ausgeführt werden können. Ich kann mir vorstellen, dass Sie deshalb in RFC 2000 keine Antworten auf Ihre Fragen gefunden haben.

@rpjohnst Danke, aber ich glaube, ich war vielleicht unklar. Ich habe bereits sowohl rust-lang/rfcs#2632 als auch rust-lang/rfcs#2000 gesehen, aber ich bin mir ziemlich sicher, dass dies in beiden nicht erwähnt wird. (aber ich könnte mich irren?) Was ich frage, sind bedingte konstante Funktionen. Siehe die Beispiele, die ich geschrieben habe, da es schwer zu beschreiben ist.

@yodaldevoid Whoops du hast recht, wo soll ich das fragen?

Was die Makrofrage angeht, stimme ich zu, dass sie jetzt, wo ich darüber nachdenke, nicht wirklich viel nützt

Was ich frage, sind bedingte const-Funktionen. Siehe die Beispiele, die ich geschrieben habe, da es schwer zu beschreiben ist.

Ihre square_const Definition kann anstelle von square (dh sie wird zur Laufzeit zu einer Funktion mit der entsprechenden Signatur gezwungen). Weitere Informationen zu diesem Verhalten finden Sie unter const fn und Merkmalsgrenzen zu stellen).

@varkor Ich bin nicht davon überzeugt, dass dies der Fall ist (da sich die Merkmalsgrenzen ändern), aber ich frage in rust-lang/rfcs#2632.

Unfallbericht:

Code:

#![feature(const_generics)]

use std::marker::PhantomData;

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

Kompilierer:

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 : const generics ist noch nicht vollständig implementiert und wird voraussichtlich nicht funktionieren. Wir werden eine Ankündigung machen, wenn es Zeit ist, mit #![feature(const_generics)] zu experimentieren (über die Sie benachrichtigt werden, wenn Sie diese Ausgabe abonniert haben).

@varkor Warte , @Jezzas Beispiel hat _marker: PhantomData<(LOWER, UPPER)> - das sind zwei const Parameter, die als Typen verwendet werden, warum hat rustc_resolve keinen Fehler erzeugt?

Guter Punkt: Ich werde das untersuchen. Beachten Sie, dass dies nur ein Problem mit #![feature(const_generics)] , also kein kritisches Problem (die Verwendung nichtgenerischer consts führt zu dem erwarteten Fehler). Vielleicht erreicht es nie rustc_resolve .

~ @eddyb : dieser ICE kommt von resolve_ident_in_lexical_scope , also stelle ich mir vor, dass er wahrscheinlich mit https://github.com/rust-lang/rust/issues/58307 zusammenhängt. ~

Bearbeiten: Eigentlich vielleicht nicht – das scheint nur für macro_rules! .

Minimiert:

#![feature(const_generics)]

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

Beheben Sie ICEs, bevor Sie den Fehler "erwarteter Typ, gefundener Wert" erzeugen.

Der Index ist hier außerhalb der Grenzen:
https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/lib.rs#L3919

Aktuelles nightly produziert "Parameter N wird nie verwendet" für den folgenden Code:

struct Foo<const N: usize> { }

Aus der vorherigen Diskussion dachte ich, compilieren sollte diesen Code akzeptieren. Ist es einfach ein Artefakt der unvollendeten Implementierung?

@newpavlov normale Typparameter müssen verwendet werden, sollte man PhantomData<n> tun können?

Ich denke, es ist bereits möglich, PhantomData<[(); N]> zu tun. Ich bin mir jedoch nicht sicher, ob das etwas ist, was wir tatsächlich durchsetzen wollen, da AFAIU der Sinn von PhantomData besteht, die Varianz zu markieren, und AFAIU gibt keinen Begriff von Varianz in Bezug auf. ein generischer const-Parameter.

Und das funktioniert nur, wenn N vom Typ usize .

Wir haben entschieden, dass es während der RFC-Diskussion nicht notwendig ist, const-Parameter zu verwenden, und die aktuelle Implementierung ist ein Fehler.

@ withoutboats Können Sie darauf hinweisen, wo im RFC das angegeben ist? Ich habe versucht, etwas in diesem Sinne zu finden und muss es übersehen haben.

Ich weiß nicht, ob es im RFC-Text enthalten war

@ withoutboats Würde es Ihnen etwas

@yodaldevoid Auf diesen Kommentar wurde bereits verwiesen: https://github.com/rust-lang/rust/issues/44580#issuecomment -419576947 . Aber es ging nicht um die RFC-PR, sondern um dieses Thema.

Ich kann den Kommentarverlauf von vor einigen Jahren jetzt nicht durchforsten, aber ich kann erklären: Die Verwendung von Typvariablen ist als Blocker erforderlich, um Sie über die Varianz dieser Parameter nachdenken zu lassen (IMO ist dies auch unnötig und wir könnten standardmäßig kovariant sein, aber das ist ein separates Thema). Konstante Parameter haben keine Wechselwirkung mit der Varianz, so dass dies keine Motivation hätte.

@HadrienG2 Vielen Dank, dass Sie einen relevanten Kommentar gefunden haben.

@ withoutboats Ich habe nicht wirklich erwartet, dass du all die Jahre der Kommentare dafür hattest .

Vielen Dank für die Erklärung. Ich muss zugeben, dass ich mich nie mit Varianz auseinandersetzen kann, egal wie oft ich versuche, sie zu lernen, aber auch ohne das macht es bei erneutem Nachdenken Sinn, keine Kostenparameter zu verwenden. Ich werde die Behebung dieses Fehlers auf unsere Liste der FIXMEs werfen.

Hier ist eine Zusammenfassung der bisherigen Fortschritte bei const-Generika.


Bevor die Arbeit an const-Generika richtig beginnen konnte, mussten einige Refactorings durchgeführt werden. @jplatte hat die Aufgabe mit https://github.com/rust-lang/rust/pull/45930 übernommen. @jplatte begann dann

@yodaldevoid und ich haben dort weitergemacht, wo @jplatte aufgehört hat, stellten aber schnell fest, dass der Fortschritt durch die Art und Weise, wie generische Parameter im Allgemeinen gehandhabt wurden, behindert wurde. So begann eine Reihe von Änderungen, um die Handhabung von Generika in der gesamten Codebasis zu überarbeiten: 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.

Damit könnte die Implementierung von const-Generika ernsthaft beginnen. Seitdem haben @yodaldevoid und ich langsam aber sicher schrittweise Unterstützung für const-Generika hinzugefügt: 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 und die meisten vor kurzem https://github.com/rust-lang/rust/pull/59008. (Diese wurden größtenteils aus der Haupt-Pull-Anfrage von


Wie ist der Stand jetzt? Einige konst Generika Tests jetzt 🎉However arbeiten, gibt es noch diejenigen , die dies nicht tun, und es gibt eine Reihe von FIXME s über den gesamten Code , dass wir noch arbeiten müssen. Wir nähern uns jetzt jedoch näher und wir sind an einem Punkt angelangt, an dem es einige niedrig hängende Früchte gibt, wenn Sie helfen möchten.

  • Zuerst brauchen wir mehr Tests. Bisher gibt es nur eine Handvoll Const-Generika-Tests , aber wir hätten gerne noch viele mehr. Da uns eine Reihe von Problemen bekannt sind, brauchen wir im Moment keine Fehlerberichte, aber wir brauchen das Bestehen von Tests. Wenn Sie einen Testfall haben, der funktioniert (und einem vorhandenen Test nicht allzu ähnlich sieht), können Sie einen Pull-Request öffnen, um ihn hinzuzufügen.
  • Zweitens gibt es, wie bereits erwähnt, eine Reihe von FIXME(const_generics) die über den Code verstreut sind. Wir planen, uns durch sie zu arbeiten, aber @yodaldevoid und ich haben nur so viel Zeit. t doppelter Aufwand).

Ich habe im oberen Post einen Überblick über einige der verbleibenden Implementierungsprobleme geschrieben, bevor const generics für die richtigen Tests bereit ist, um eine grobe Vorstellung davon zu geben, was noch zu tun ist.

Es hat seine Zeit gedauert, aber wir machen stetig Fortschritte und hoffen, das Tempo mithalten zu können. Es ist motivierend, endlich zu sehen, wie sich die Dinge fügen!

Ich habe Monate für diesen Beitrag @varkor erwartet :) Ich bin kein Rust-Zauberer, aber ich würde gerne einen der FIXME s tackle angehen

Herzlichen Glückwunsch @jplatte , @yodaldevoid und @varkor. Dies ist ein großer Schritt, um benutzerdefinierte Array-like Merkmale in mathematischen Bibliotheken loszuwerden.

Cross-Posting...

@varkor In Bezug auf Tests wäre es vielleicht hilfreich zu wissen, was voraussichtlich funktioniert. Ich war zum Beispiel überrascht, dass das nicht funktioniert: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=d84ffd15226fcffe02c102edb8ae5cf1

Auch als Referenz für FIXME-Interessierte: https://oli-obk.github.io/fixmeh/

@mark-im versuche {} um FOO . Auf diese Weise wird es funktionieren.

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

Wenn ein Ausdruck als const-Parameter (außer bei Arrays) angewendet wird, der kein Identitätsausdruck ist, muss der Ausdruck in einem Block enthalten sein. Diese syntaktische Einschränkung ist erforderlich, um zu vermeiden, dass beim Parsen eines Ausdrucks innerhalb eines Typs ein unendlicher Lookahead erforderlich ist.

{expression} sollte nur notwendig sein, wenn der Ausdruck kein Bezeichner oder Literal ist, das ist ein Fehler.

Zitat des gleichen RFC:

Identitätsausdruck: Ein Ausdruck, der nicht weiter ausgewertet werden kann, außer indem er durch Namen im Gültigkeitsbereich ersetzt wird. Dies beinhaltet alle Literale sowie alle Idents - zB 3, "Hello, world", foo_bar.

@mark-im Ja, derzeit können wir beim ersten Parsen nicht den Unterschied zwischen Idents für Typen und Konstanten erkennen. Wir haben die Entscheidung getroffen, wie wir all das in Zukunft handhaben. Ich nehme an, jetzt könnte die Zukunft sein.

Für einen kleinen Hintergrund haben wir über zwei Möglichkeiten gesprochen, an die ich mich erinnern kann, wie man damit umgeht. Die erste Möglichkeit besteht darin, die Benutzer zu zwingen, const-Argumente zu markieren (entweder durch Eingabe von const vor ihnen oder auf andere Weise). Dies ist aus ergonomischer Sicht nicht großartig, aber aus Sicht des Parsens einfach. Die zweite Möglichkeit besteht darin, alle generischen Argumente gleich zu behandeln, bis wir später während der Kompilierung beginnen, sie mit generischen Parametern zu paaren. Dies ist wahrscheinlich die Methode, die wir wählen möchten, und es wurde bereits daran gearbeitet, dies zu ermöglichen, wir haben nur den letzten Schritt noch nicht getan.

Erstaunliche Arbeit. Ich helfe gerne, indem ich einige der FIXME 's repariere, wenn ich kann. Ich habe überhaupt nicht viel Erfahrung mit der rustc-Codebasis, also beginne ich mit dem FIXME https://github.com/rust-lang/rust/blob/master/src/librustc_mir/monomorphize/item.rs# L397 , wie es scheint, wäre es einfach.

Was ist mit folgendem Code:

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

Derzeit führt dies zu einem Kompilierungsfehler "kein zugeordnetes Element namens N für Typ Self im aktuellen Bereich gefunden". Wird ein solcher Code akzeptiert, nachdem FIXME s beendet wurden, oder erfordert die Implementierung zusätzlichen Aufwand?

@yodaldevoid

Kurze Frage, bitte entschuldigt, falls dies schon besprochen wurde.

Die zweite Möglichkeit besteht darin, alle generischen Argumente gleich zu behandeln, bis wir später während der Kompilierung beginnen, sie mit generischen Parametern zu paaren. Dies ist wahrscheinlich die Methode, die wir wählen möchten, und es wurde bereits daran gearbeitet, dies zu ermöglichen, wir haben nur den letzten Schritt noch nicht getan.

Läuft dies nicht eher gegen den Strich nach dem Prinzip, dass Rust Funktionssignaturen explizit macht, um Fehler zu vermeiden, die sich auf die Implementierung einer Funktion beziehen? Vielleicht verstehe ich dies jedoch völlig falsch und Sie sprechen davon, generische Parameter innerhalb des Funktionskörpers zu analysieren.

Läuft dies nicht eher gegen den Strich nach dem Prinzip, dass Rust Funktionssignaturen explizit macht, um Fehler zu vermeiden, die sich auf die Implementierung einer Funktion beziehen? Vielleicht verstehe ich dies jedoch völlig falsch und Sie sprechen davon, generische Parameter innerhalb des Funktionskörpers zu analysieren.

Hier geht es darum zu bestimmen, ob Bezeichner, die als generische Parameter an eine Funktion übergeben werden, Konstanten oder Typen sind.

Beispiel:

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

Beachten Sie, dass Sie beim Definieren der Funktion angeben müssen, dass NAME eine Konstante ist, aber beim Aufrufen muss nicht angegeben werden, dass HIS_NAME eine Konstante innerhalb des Turbofish-Operators ( ::< > ) ist.

@zesterer Wenn dieser Lösungsvorschlag für den Benutzer transparent gemacht wurde (z. B. bei der Definition einer Funktion gab es keinen Unterschied zwischen Parametertypen), liegen Sie richtig. Die Idee hinter der vorgeschlagenen Lösung besteht jedoch nicht darin, das Benutzerverhalten zu ändern, sondern nur die Implementierung zu ändern. Der Benutzer würde immer noch Funktionssignaturen mit expliziten type-, const- und life-time-Parametern schreiben, und generische Argumente müssten immer noch mit einem entsprechenden Parameter übereinstimmen, nur wie wir sie im Compiler parsen, würde sich ändern.

@newpavlov Ich bin überrascht, dass der Code nicht funktioniert. Wenn Sie eine separate Ausgabe einreichen könnten, wäre dies willkommen.

@yodaldevoid @robarnold

Ach, hab dich. Ich habe fälschlicherweise angenommen, dass es sich um Typ- / Funktionssignaturen handelt.

Für die Verwendung von const-Generika für integrierte Array-Typen habe ich #60466 geöffnet, um die Meinungen anderer dazu zu sehen.

Impl-Blöcke scheinen im Moment komplett kaputt zu sein. Ist dies im aktuellen Status der Funktion zu erwarten oder sollte ich ein anderes Problem dazu eröffnen?

Impl-Blöcke scheinen im Moment komplett kaputt zu sein. Ist dies im aktuellen Status der Funktion zu erwarten oder sollte ich ein anderes Problem dazu eröffnen?

Sie müssen {} s um die N , impl<const N: usize> Dummy<{N}> {} hinzufügen.

Aber ja, dann erhalten Sie eine Fehlermeldung über den nicht eingeschränkten Parameter.

Ah danke. Die Sache mit den Zahnspangen vergessen!

Ich bin nicht überrascht, dass es fehlschlägt, sobald das gelöst ist, da dies ein stark minimierter Testfall war.

...aber ja, das sollte wohl funktionieren:

#![feature(const_generics)]

trait Dummy {}

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

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

...und es schlägt immer noch mit E0207 fehl:

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.

... und ich gehe davon aus, dass

#![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
Siehe #60619 und #60632.

Ich denke, dieser Code sollte bauen:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e45b7b5e881732ad80b7015fc2d3795c

aber es fehler derzeit:

error[E0119]: widersprüchliche Implementierungen von Trait std::convert::TryFrom<[type error]> für Typ MyArray<_, _> :

Leider ist das eine Einschränkung von Try{From,Into} und hat nichts mit const-Generika zu tun. Sie können in vielen Fällen nicht generisch implementiert werden: Laufstall

@oberien in Ihrem Beispiel ist T ein fremder Typ, und es ist nicht bekannt, ob T: Into<Foo<T>> gilt oder nicht.

In meinem Beispiel [T; N] ist ein in libcore definierter Typ und ist #[fundamental] (ich weiß nicht einmal, was das bedeutet), also denke ich, dass der Trait-Resolver tatsächlich weiß, dass [T; N]: Into<MyArray<T, {N}>> nicht gilt, und da sollte kein Konflikt sein, denke ich?

[...] und ist #[fundamental] [...]

Dies ist das Problem, fundamental hängt damit zusammen, dass nachgeschaltete Benutzer Trait-Implementierungen definieren können, wenn sie einen lokalen Typ in den grundlegenden Wrapper-Typ einschließen. In diesem Playground können Sie sehen, dass Ihre Implementierung zwischen nicht fundamentalen Typen funktioniert, aber fehlschlägt, wenn der Typ from mit fundamental markiert ist (mit einer viel besseren Fehlermeldung).

@varkor @yodaldevoid Also, ich landete gerade in einer Situation, in der S<{N == 0}> (wobei S einen const bool Parameter und N einen const usize ) ist aus Sicht des Compilers weder S<{true}> noch S<{false}> (in dem Sinne, dass er nicht die gleichen Eigenschaften wie beide implementiert) und ich bin mir nicht sicher, ob dies ein Fehler oder ein Fehler ist erwartete Einschränkung des aktuellen Prototyps. Können Sie mir eine kurze Auffrischung der aktuellen Vereinigungslogik geben?

Als minimiertes Beispiel ist dies...

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

... erzeugt folgenden Fehler:

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

Ich weiß es nicht genau, aber ich denke, das Problem könnte sein, dass der Compiler nicht die Logik hat, um zu entscheiden, ob das Literal 0 ein u8 oder ein u16 . Probiere etwas wie: 0u8 as _

Das ist es nicht, die Fehlermeldung bleibt die gleiche.

Wenn Sie jedoch eine Version wünschen, die keine Inferenz vom Ganzzahltyp enthält, ist hier eine dümmere Minimierung:

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
}

Scheitert immer noch, weil Test<{B}> angeblich Not nicht implementiert hat.

Hi ! Ich bin mir nicht wirklich sicher, ob die Vereinheitlichung auf konstanten Werten funktionieren soll oder nicht, aber es ist ein neues Vereinheitlichungssystem in Arbeit ("Kreide"), an dem gearbeitet wird, aber nicht das ist, was rustc derzeit verwendet.
Also, was Sie versuchen zu tun, muss _vielleicht_ warten, bis die Kreide fertig ist. Und selbst dann bin ich mir nicht sicher, ob es auch mit Kreide funktioniert oder funktionieren soll.
Informationen zum Fortschritt finden Sie unter

@HadrienG2 könnten Sie dafür ein separates Problem

@carado Unification wird auf consts durchgeführt oder nichts davon würde funktionieren. Const-Generika werden nicht auf Kreide blockiert.

@HadrienG2 sobald wir die Spezialisierung für const-Generika haben, können Sie so etwas tun wie

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
}

Was Sie tun möchten, ist eine Art Vollständigkeitsprüfung, wie sie es für ein Match gibt, das gerade auf das Eigenschaftensystem erweitert wurde. Weiß nicht, dass es in irgendeinem Thread erwähnt wird.

@carado Chalk betrifft nicht die tatsächlichen "Typsystem-Primitive", von denen dies eine ist.
Das heißt, rustc noch (auch heute, da Chalk bereits teilweise integriert ist) den Teil der Vereinheitlichung implementieren, der sich mit für Chalk undurchsichtigen Entitäten befasst (zB zwei verschiedene Konstantenausdrücke).
Wenn wir das implementieren (wofür wir sowieso keine Pläne für die nahe Zukunft haben), wird es sich nicht viel ändern, selbst nachdem Chalk das Eigenschaftssystem ersetzt hat (was sein Hauptzweck ist, nicht die Vereinheitlichung).

Oh, mein Böses dann! Ich glaube, ich weiß nicht wirklich, wovon ich rede.

Eines der großartigen Dinge, die const generics bieten würde, sind kompilierte einfache Funktionen, zum Beispiel faktorielle.

Beispiel:

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

Soweit ich weiß, gibt es eine begrenzte Unterstützung für const-Generika in Nightly? Wenn ja, gibt es einen Ort, der besser organisiert ist als diese Ausgabe, an dem ich finde, wie man ihn verwendet und so?

@dancojocaru2000 const Funktionen sollten der bevorzugte Weg für die Berechnung auf Wertebene zur Kompilierzeit sein, zB https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4994b7ca9cda0bfc44f5359443431378 , was passiert nicht funktionieren, weil sie noch nicht vollständig implementiert sind. Aber auch const Generika sind es auch nicht.

Ihr Snippet könnte problematisch sein, weil ich nicht sicher bin, ob match für const Argumente funktionieren soll. Sie haben im Code auch Addition mit Multiplikation gemischt, aber das spielt keine allzu große Rolle.

Ich denke, Sie können mit einer Peano-Codierung bereits zur Kompilierzeit Fakultäten durchführen, die oft als Demo für andere funktionale Sprachen gezeigt wird.

Kompilierzeit berechnete einfache Funktionen

Ich denke das passendere Feature wäre const fn .

Theoretisch würde Ihr Code fast so funktionieren, wie er ist

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

Aber es geht nicht:

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`

Obwohl Sie wirklich einen unsignierten Typ verwenden sollten:

#![feature(const_generics)]

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

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

Gibt es einen Ort, der besser organisiert ist als diese Ausgabe, wo ich finde, wie man es benutzt und so?

Das wird normalerweise im instabilen Buch behandelt , aber jetzt gibt es dort nichts Nützliches. Wenn Sie Teile entdecken, könnten Sie vielleicht darüber nachdenken, einige Inhalte dafür zu skizzieren?

Fakultäten zur Kompilierzeit mit einer Peano-Kodierung,

Ein Beispiel dafür finden Sie in der typenum-Kiste

Theoretisch würde Ihr Code fast so funktionieren, wie er ist

Beim Aufrufen von factorial::<0> das _ => factorial::<{X - 1}>() * X zu kodiert sein, oder? Dies würde jedoch zu einem Integer-Unterlauf führen.

Kann ich erwarten, dass Code wie dieser in Zukunft kompiliert wird?

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

Dies kreuzt sich mit einer kürzlich durchgeführten Implementierung einiger [T; N] Arrays für N <= 32 als konstante Generika, aber der folgende Code kompiliert nicht auf einem neuesten ( 4bb6b4a5e 2019-07-11 ) Abend mit verschiedenen Fehlern 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;

obwohl folgendes gilt:

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

Anscheinend ist N*2 keine gültige Konstante oder kein gültiger Qualifizierer für eine solche Einschränkung

@shamatar N*2 sollte mit geschweiften Klammern wie `[u64; {N*2}]

Ich denke, die Fehler "Trait nicht implementiert" kommen von den Ableitungsmakros, die die Grenze für [u64; {N*2}] nicht hinzufügen, aber wenn die Ableitungen entfernt werden, gibt es derzeit einen ICE:

```Fehler: interner Compilerfehler: Konstante im Typ hatte einen ignorierten Fehler: TooGeneric
--> src/main.rs:5:1
|
5 | / pub struct BigintRepresentation<
6 | | const N: verwenden
7 | | >(Pub [u64; N])
8 | | wobei [u64; N]: std::array::LengthAtMost32,
9 | | [u64; {N*2}]: std::array::LengthAtMost32;
| |_____________________________________________^

Thread 'rustc' geriet in Panik bei 'keine Fehler aufgetreten, obwohl delay_span_bug ausgegeben wurde', src/librustc_errors/lib.rs:366:17
Hinweis: Führen Sie mit der Umgebungsvariablen RUST_BACKTRACE=1 , um einen Backtrace anzuzeigen.

Dieser Code kompiliert nicht:

#![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 musst du Foo<{X}>

@pengowen123 Ja, es kompiliert (stürzt den Compiler mit constant in type had an ignored error: TooGeneric ) ohne derive , aber dann kann es eine separate Frage sein, ob eine solche "doppelte Einschränkung" effektiv N <= 32 ist oder nicht N <= 16 vom Compiler nicht angewendet.

Ausdrücke, die Parameter erwähnen, werden wahrscheinlich noch eine Weile nicht funktionieren, [T; N] und Foo<{N}> werden so geschrieben, dass sie keine Ausdrücke mit der Erwähnung von N , sondern direkt auf den Parameter N verweisen und das größere Problem umgehen.

Die Verwendung von Self führt zu einem Absturz.
Selbst wenn Sie es gegen Value<{C}> austauschen, stürzt es immer noch ab.

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

Wie bei #61338 ist die Ursache des Problems die inkrementelle Kompilierung.

Kompilierzeit berechnete einfache Funktionen

Ich denke das passendere Feature wäre const fn .

Theoretisch würde Ihr Code fast so funktionieren, wie er ist

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

Aber es geht nicht:

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`

Obwohl Sie wirklich einen unsignierten Typ verwenden sollten:

#![feature(const_generics)]

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

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

Diese Funktion ist auch für mich nützlich, const fn ist nicht genug. Ich möchte es für ein N-dimensionales Array mit einer Abschlussbedingung von dimensions=0 verwenden.

Ich kann eine Struktur erstellen:

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

Dies schlägt fehl, weil ich das Array aus folgenden Gründen nicht initialisieren kann:

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

Ich habe versucht, es zu umgehen, indem ich const-Werte auf den generischen Wert gesetzt habe.

Das geht über den obigen Fehler hinaus, aber der Compiler stürzt dann ab.

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=[]

Die Sprache benötigt dringend grundlegende Konstanten-Generika, damit die Standardbibliothek nicht jede Funktion für jede Array-Größe definieren muss. Dies ist der einzige Grund, warum ich keinen Rost verwende. Brauchen wir wirklich komplette Kompilierzeitfunktionen?
Ich könnte mich irren, aber einfache Integer-Ausdrücke sollten ausreichen und ich hoffe, dass niemand seine Zeit damit verschwendet, sicherzustellen, dass diese verrückten Beispiele funktionieren.

Die Sprache benötigt dringend grundlegende konstante Generics, damit die Standardbibliothek nicht jede Funktion für jede Array-Größe definieren muss

Da gibt es schon einige Bemühungen https://github.com/rust-lang/rust/issues/61415.

Ich hoffe, niemand verschwendet seine Zeit damit, dass diese verrückten Beispiele funktionieren.

Manche Leute tun das, ich kann kein Problem damit sehen ;)

Dies ist der einzige Grund, warum ich keinen Rost verwende.

Dies ist der am wenigsten interessante Grund, den ich für jemanden zitiert habe, der keinen Rost verwendet. Sind Sie sicher, dass Sie die Wahrheit sagen?

aber einfache ganzzahlige Ausdrücke sollten ausreichen

Selbst diesen reduzierten Umfang zu realisieren ist unglaublich schwer. Probieren Sie es aus, wir haben wirklich fähige Leute, die das machen und es gibt einen Grund, warum es so lange dauert.

Leider konnte ich in letzter Zeit nicht viel Zeit auf die Behebung von Fehlern in Const-Generika (oder allgemeiner auf Rust) verwenden. Wenn sich jemand daran beteiligen möchte, const-Generika voranzutreiben, gebe ich gerne Ratschläge, um die offenen Probleme anzugehen und Fehlerkorrekturen zu überprüfen, obwohl es wahrscheinlich eine Weile dauern wird, bis ich mich wieder darauf konzentrieren kann.

Ich hoffe, niemand verschwendet seine Zeit damit, dass diese verrückten Beispiele funktionieren.

Niemand ist es, miri ist bereits viel mächtiger als C++ constexpr .
Und niemand arbeitet an etwas Ausgefallenem wie der Ableitung von N = M aus N + 1 = M + 1 .

Die meisten dieser Fehler betreffen nicht die Ausdrücke in ihnen, sondern das Typsystem und wie const Generika mit allen anderen Funktionen interagieren.
Sie wären immer noch da, wenn Sie nur const Generika und Integer-Literale hätten.

Übrigens, ich denke, der Fehler "Array-Längen können nicht von generischen Parametern abhängen" für [expr; N] Ausdrücke wird nicht benötigt. Wir könnten den gleichen Trick wie für [T; N] Typen verwenden und ziehen N ohne es als Ausdruck auszuwerten.

Ich möchte das zwar versuchen, aber ich bin mir nicht sicher, ob ich die richtige Person bin. Ich habe wenig Rost verwendet und kenne sehr wenig Compilertheorie. Ich brauche vielleicht ein bisschen Coaching, aber ich bin auf jeden Fall bereit. 😄

Bearbeiten: Ich habe jedoch ein bisschen Erfahrung mit Software im Allgemeinen.

@varkor , ich habe auf der Suche nach etwas Nützlichem für rustc gesucht und würde gerne einsteigen und helfen. Vielen Dank auch für Ihre Offenheit, dass Sie sich Zeit dafür nehmen!

@varkor , ich

Eine Sache, die wir jetzt tun könnten, ist mir gerade klar geworden, dass Foo::<{...}> / [expr; ...] Ausdrücke auf generische Parameter verweisen dürfen (im ... Teil).

Dies liegt daran, dass Ausdrücke irgendwo innerhalb eines Bodys verschachtelt werden müssen , was dazu neigt , zyklische Abhängigkeiten zu vermeiden.

Allerdings mache ich mir Sorgen, dass zB [(); [0; 1][0]] in einer Signatur kaputt gehen würde, so dass wir wahrscheinlich sowieso einen Kraterlauf brauchen

Für jeden, der daran interessiert ist, bei const-Generika zu helfen, würde ich empfehlen, einen Blick auf die Liste der offenen Probleme mit const-Generika zu werfen und zu untersuchen, was für Sie interessant ist. Einige haben bereits eine Untersuchung durchgeführt, die in den Kommentaren stehen sollte. Die meisten Probleme werden wahrscheinlich ein wenig Nachforschung erfordern. Es ist hilfreich, einen Kommentar zu hinterlassen, wenn Sie vorhaben, etwas zu untersuchen, damit wir keine doppelten Anstrengungen unternehmen (aber Sie können den Problembevollmächtigten oft ignorieren, wenn es für eine Weile keine Aktivitäten gab). Wenn Sie Fragen haben, können Sie diese in den Kommentaren zum Thema oder auf Discord oder Zulip stellen. @eddyb , @yodaldevoid , @oli-obk und ich kennen viele der relevanten Bereiche und sind gute Ansprechpartner. Vielen Dank für Ihr Interesse!

cc @hameerabbasi , @ranweiler

Fragen zu const Generika:

  1. Wo finde ich Unterlagen (damit ich hier keine Fragen stellen muss)
  2. Welche Einschränkungen gibt es derzeit bei der Verwendung eines const Parameters beliebigen Typs?
  3. Welche wichtigen Funktionen von const Generika sind nicht implementiert / stürzen den Compiler ab?

PS (Mitwirkende:) Vielen Dank für die Arbeit an dieser Funktion.

const-generics befindet sich noch in der Entwicklung, daher gibt es noch keine offiziellen Dokumente dazu. Bei den anderen beiden Fragen bin ich mir nicht ganz sicher. Als ich das letzte Mal const-Generics verwendet habe, sind sie abgestürzt, als ich einige const-Generic-Parameter angegeben habe, aber es ist fast einen Monat her, seit ich das letzte Mal etwas damit gemacht habe, also können sich die Dinge seitdem geändert haben.

Und niemand arbeitet an etwas Ausgefallenem wie der Ableitung von N = M aus N + 1 = M + 1 .

Dies wäre sehr nützlich, um einen Solver für solche Typbeschränkungen zu haben. Wenn beispielsweise eine Addition mit zwei N-Bit-Zahlen implementiert wird, sollte die Rückgabegröße (N + 1) sein, um ein Überlaufbit zu berücksichtigen. Wenn (zum Beispiel) zwei 5-Bit-Zahlen angegeben werden, sollte der Löser überprüfen, ob N + 1 = 6 . Hoffentlich kann dies später auf const-Generika geschraubt werden :)

@flip111 Ja, ich denke, der Plan ist, dies später hinzuzufügen, aber diese Art von allgemeinen Ausdruckseinschränkungen sind sehr komplex und schwer zu implementieren. Also werden wir sie vielleicht für ein paar Jahre nicht sehen.

Um ehrlich zu sein, klingt die Lösung dieser Art von Constraint-basierten Problemen sehr nach einem Job für Prolog. Vielleicht ist Huckepack auf der Chalk-Engine eine Option? Afaik löst es jedoch Eigenschaften, oder?

Übrigens, ich liebe das Thema, obwohl ich es mir nicht leisten kann, bei diesem Geldautomaten zu helfen. Vielen Dank an alle, die an Const Generics arbeiten, für Ihre Zeit und Ihr Engagement. 💐

Ein kleines Update: Ich arbeite mich den Rust Tree hoch. Ich habe vor, einen Beitrag zu leisten, möchte aber ein Selbststudium an dem Ort machen, an dem ich kein übermäßiges Coaching brauche.

Ich kann eine Struktur erstellen:

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

Dies schlägt fehl, weil ich das Array aus folgenden Gründen nicht initialisieren kann:

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

Ich bin auf das gleiche Problem gestoßen, habe aber eine Problemumgehung mit MaybeUninit :
https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=3100d5f7a4efd844954a6fa5e8b8c526
Es ist offensichtlich nur ein Workaround, um richtig initialisierte Arrays zu erhalten, aber für mich reicht dies aus, bis ein geeigneter Weg zur Verfügung gestellt wird.
Hinweis: Ich denke, der Code sollte immer wie beabsichtigt funktionieren, aber wenn jemand einen Fehler bei der Verwendung des unsicheren findet, würde ich ihn gerne beheben.

@raidwas Sie = , um nicht initialisierten Speicher zu initialisieren. Tun Sie dies stattdessen,

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

@KrishnaSannasi danke, guter Fang: D (technisch gesehen habe ich keinen nicht initialisierten Speicher

Technisch gesehen ist das Fallenlassen sogar von Floats undefiniert, aber es ist derzeit nicht ausnutzbar.

Technisch gesehen ist das Fallenlassen sogar von Floats undefiniert, aber es ist derzeit nicht ausnutzbar.

Ich hätte erwartet, dass das Löschen eines beliebigen Copy Typs, auch nicht initialisierter, ein No-Op ist. Vielleicht lohnt es sich, das zu ändern.

Technisch gesehen ist das Fallenlassen sogar von Floats undefiniert, aber es ist derzeit nicht ausnutzbar.

Ich hätte erwartet, dass das Löschen eines beliebigen Copy Typs, auch nicht initialisierter, ein No-Op ist. Vielleicht lohnt es sich, das zu ändern.

Es ist immer noch technisch UB. Während das sichere Löschen von definierten Copy Werten ein No-Op ist, kann der Compiler entscheiden, einige unerwartete Optimierungen vorzunehmen, wenn Sie versuchen, nicht initialisierten Speicher jeglicher Art zu löschen (zB den gesamten Code entfernen, der diesen Wert jemals berührt). könnte Dinge kaputt machen. Das ist mein Verständnis davon.

Um nicht unhöflich zu sein, aber ≥ 60 Leute werden über Kommentare zu diesem Thread benachrichtigt, daher sollten wir die Diskussion außerhalb des Themas wahrscheinlich auf ein Minimum beschränken. Nutzen wir das lieber, um die Arbeit an const-Generika zu koordinieren, denn das wollen wir alle.

Absturz, wenn ich _Aliases verwendet habe, die auf Typ mit const-param_ von einer anderen Kiste zeigen (Abhängigkeit).

Zum Beispiel:

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

    ![Feature(const_generics)]

Pub-Typ Alias= Struktur;
pub struct Struct(T);

- crate B

externe Kiste crate_a;
benutze crate_a::Alias;

pub fn inner_fn(v: Alias) {}
```
Absturzprotokoll (44580).txt

@fzzr- klingt wie #64730

Ich habe etwas Code auf Const-Generika umgestellt und es scheint, als gäbe es wirklich zwei verschiedene Anwendungsfälle. Ich bin mir nicht sicher, ob sie zusammengeführt werden sollten oder ob wir besser mit zwei verschiedenen Syntaxen für die Anwendungsfälle bedient sind.

Viele meiner Verwendungen sind nicht wirklich für Typen gedacht, bei denen ein konstanter Wert eine Rolle bei der Bestimmung der Typen spielt, sondern um Funktionen zu nutzen, die für nicht konstante / literale Werte gesperrt sind (noch nicht vollständig unterstützt, z. aber letztendlich muss es sein).

IMHO sollten wir formell "const arguments" neben const generics landen, damit die Leute keinen mutierten Code schreiben, der tausend "const generics" (eine für jedes Argument) einführt, um den Compiler dazu zu bringen, bestimmte Variablen als Literale / Konstanten auszuwerten.

@mqudsi Könnten Sie ein Beispiel geben? Es gibt bereits Pläne und laufende Grundlagenarbeit, um const eval leistungsfähiger zu machen. Das ist jedoch orthogonal zu const-Generika.

Was ich meine ist, wenn Sie typischen Parser-Code wie den folgenden zur Wiederverwendung umgestalten möchten:

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

Normalerweise würden Sie es in eine Funktion verschieben, außer dass Folgendes nicht funktioniert, da bestimmte Werte, die beim Mustervergleich verwendet werden, zu Variablen geworden sind:

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

    ...
}

Wenn const generisches vor const-Argumenten landet, kann ich const generisches plötzlich missbrauchen, um Folgendes zu erreichen:

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

    ...
}

Ab heute funktioniert selbst dieser Code nicht, weil der Compiler die generischen const-Werte nicht als Konstanten erkennt, die für die Verwendung in einem Muster gültig sind, aber das ist offensichtlich falsch und muss behandelt werden, bevor RFC 2000 landen kann. Versteh mich nicht falsch, ich kämpfe seit Jahren für generische Konstanten und habe PRs bereit für ein Dutzend große Kisten (ich kann es kaum erwarten, die Zeitzone in chrono einer konstanten generischen und alle verschiedenen DateTime Typen vereinen), aber imho, wenn es möglich wird, const-Generika zu missbrauchen, um const-Argumente zu fälschen (wobei mit "const" wirklich "wörtlich" gemeint ist), dann werden Sie weit verbreitet sein Missbrauch davon.

Es ist nicht unbedingt das Ende der Welt, aber ohne zu tief in die Materie einzudringen, scheint es, als ob eine ordnungsgemäße und vollständige Implementierung von const-Generika sowieso notwendigerweise alle Installationen für const-Argumente umfasst, also können wir uns genauso gut die zusätzliche Zeit nehmen um die Syntax/ux/story für const-Argumente fertigzustellen, während wir schon dabei sind, und eine unglückliche Ära des Code-Gestanks zu vermeiden. Ja, das oben Genannte kann immer noch mit Makros durchgeführt werden, aber die Ergonomie von const-Generika ist tausendmal einfacher.

fwiw, so stelle ich mir die const-Argumentversion vor:

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

    ...
}

Es wäre praktisch identisch mit const-Generika, aber für die Semantik.

Normalerweise würden Sie es in eine Funktion verschieben, außer dass Folgendes nicht funktioniert, da bestimmte Werte, die beim Mustervergleich verwendet werden, zu Variablen geworden sind:

Dies scheint ein Anwendungsfall zu sein, der leichter angegangen werden kann, indem die Werte gebundener Variablen in Mustern (nicht nur in consts) mit einer neuen Syntax verwendet werden können. Die Tatsache, dass Konstanten derzeit in Mustern erlaubt sind, ist kontraintuitiv und meines Wissens historisch.

@mqudsi Verzeihen Sie mir, wenn dies eine dumme Frage ist, aber ich sehe nichts Falsches an dem von Ihnen angegebenen Beispiel. Es sieht für mich nach einem vollkommen gültigen Anwendungsfall für const-Generika aus: eine Definition zu haben, die so verallgemeinert ist, dass sie mit beliebigen Werten als Max/Min arbeitet. Ich sehe nicht wirklich die Vorteile von const-Argumenten gegenüber const-Generika. Sie scheinen mir gleichwertig zu sein; das heißt, const-Argumente könnten einfach als Entzuckerung zu const-Generika implementiert werden. Könnten Sie näher erläutern, was mit diesem Designmuster nicht stimmt?

Hier ist eine Zusammenfassung der Arbeiten an const-Generika seit dem letzten Update . Beim letzten Mal drehte sich alles um die Kernimplementierung: sicherzustellen, dass alles zusammenpasst und einige der grundlegenden Testfälle bestanden. Seitdem konzentrierten sich die Bemühungen darauf, const-Generika zuverlässiger zu machen: Fälle zu beheben, die den Compiler abstürzten oder unerwartet nicht funktionierten, oder die Diagnose zu verbessern. Wir nähern uns etwas, das zuverlässig funktioniert, obwohl es noch ein Weg vor uns ist.


  • @skinny121 hat im letzten Monat fantastische Arbeit geleistet, um verschiedene offene Probleme mit const-Generika zu beheben:

    • Erweiterung der Testsuite (https://github.com/rust-lang/rust/pull/60550)

    • Beheben von Typinferenzproblemen (https://github.com/rust-lang/rust/pull/64679, https://github.com/rust-lang/rust/pull/65579)

    • Unterstützung weiterer Typen in const-Generika, einschließlich Strings und Slices (https://github.com/rust-lang/rust/pull/64858) und Zeiger (https://github.com/rust-lang/rust/pull/64986 .) ) (beachten Sie jedoch, dass diese wahrscheinlich nicht sofort zusammen mit dem Rest der const-Generika stabilisiert werden, da sie im ursprünglichen RFC nicht unterstützt werden)

    • Beheben von Diagnoseproblemen mit const-Generika (https://github.com/rust-lang/rust/pull/65154, https://github.com/rust-lang/rust/pull/65579)

    • Behebung einer Reihe von Problemen bei der Verwendung von const generischen Cross-Crate (https://github.com/rust-lang/rust/pull/65365)

  • @eddyb hat einige Probleme mit der const-Auswertung behoben, die sich auf const-Generika auswirkten (https://github.com/rust-lang/rust/pull/63497)
  • @matthewjasper hat einige Probleme bei der Verwendung von const-Generika in Makros behoben (https://github.com/rust-lang/rust/pull/63083)
  • @davidtwco hat ein Problem mit const-Generika und
  • @GuillaumeGomez hat die Anzeige von const-Generika in Rustdoc korrigiert (https://github.com/rust-lang/rust/pull/61605)
  • @varkor hat einige Probleme mit der Typinferenz behoben (https://github.com/rust-lang/rust/pull/61570, https://github.com/rust-lang/rust/pull/60742, https://github. com/rust-lang/rust/pull/60508), ein Problem, das die Verwendungsmöglichkeiten von const-Generika einschränkt (https://github.com/rust-lang/rust/pull/60717) und verschiedene Compiler-Abstürze (https:// /github.com/rust-lang/rust/pull/61380, https://github.com/rust-lang/rust/pull/61333, https://github.com/rust-lang/rust/pull/60710 )

Darüber hinaus wurden durch andere Arbeiten am Compiler einige der anderen Probleme im Zusammenhang mit const-Generika behoben.

Wir haben auch damit begonnen, const-Generika innerhalb des Compilers selbst zu verwenden: Array-Trait-Implementierungen verwenden jetzt dank der Arbeit von @crlf0710 und @scottmcm (https://github.com/rust-lang/rust/pull/60466, https ://github.com/rust-lang/rust/pull/62435), die zu Leistungsverbesserungen geführt hat und die es uns auch ermöglichen wird, Array-Trait-Implementierungen in Zukunft (wenn const-Generika stabilisiert ist) einzuschränken. @Centril hat den gleichen Ansatz verwendet, um VecDeque zu verbessern (https://github.com/rust-lang/rust/pull/63061).


Viele der besonders häufigen Fehler bei const-Generika wurden in den letzten Monaten behoben. @nikomatsakis untersucht die faule Normalisierung , die eine Vielzahl der verbleibenden Probleme beheben sollte, während @jplatte versucht , die Disambiguierung von const-Parametern von Typparametern zu beheben (damit Sie nicht mehr {X} eingeben müssen für a const-Argument).

Ich möchte @eddyb auch für all ihr Mentoring und ihre Überprüfung für const-Generika während der gesamten Entwicklung danken, was von unschätzbarem Wert war.

Es gibt noch eine ganze Reihe anderer Probleme zu lösen und wie zuvor könnten wir jede Hilfe gebrauchen, die wir bekommen können – wenn Sie daran interessiert sind, eines der verbleibenden Probleme anzugehen, können Sie mich gerne zu diesem Problem anpingen oder weiter Discord oder Zulip für Hinweise zum Einstieg.

@mqudsi @mark-im Wäre es angebracht, die aktuelle syntaktische Alternative für Generika, impl Trait in Argumentposition, auf const-Generika zu erweitern? Dies ist die Syntax @mqudsi , die für diesen Anwendungsfall vorgeschlagen wird.

Persönlich denke ich, dass dies die Lesbarkeit solcher Anwendungsfälle verbessern würde, aber ich sehe ein Problem: normale Generics werden verwendet, um Daten zu übergeben, also sind sie an ein Argument gebunden. Dies ist bei const-Generika nicht der Fall. Wie würden Sie sie also bestehen? So tun, als wären es Argumente?
Im Fall von impl Trait in Argumentposition kann es auch nicht mit der Turbofish-Syntax verwendet werden. Für const im Argument wäre es umgekehrt. Dies kann verwirrend sein.

Ich bin mir nicht sicher, ob die Vorteile die "Seltsamkeit" hier überwiegen, aber ich wollte trotzdem meine Gedanken teilen.

Ich habe mich vage an eine Diskussion darüber erinnert und jetzt habe ich sie gefunden: https://internals.rust-lang.org/t/pre-rfc-const-function-arguments/6709

@PvdBerg1998 das scheint eine sehr schlechte Idee zu sein. Ich verstehe wirklich nicht ganz, dass <…> eine _harte Syntax_ zum Lesen ist. Hier zwei Möglichkeiten:

  1. Du denkst wie ich, dass es auch mit mehreren Grenzen nicht schwer zu lesen ist.
  2. Sie denken, es ist schwer zu lesen: Dann wäre die Lösung vielleicht, sie auf eine where Klausel zu setzen?

Vergleichen Sie nun mit dem oben verlinkten Pre-RFC. Es würde ein _tag_ (im Sinne des Typsystems) direkt an der Position von Funktionsargumenten, dh Variablenbindungen für den Rumpf einer Funktion, einführen.

Mit anderen Worten: Es ist sehr verwirrend und wird, wie mehrere Leute für Impl Trait in Arg-Position angegeben haben, nicht benötigt. Bitte machen Sie den gleichen Fehler nicht zweimal, um eine Funktion hinzuzufügen, die sich nicht lohnt. Vor allem, wenn Sie „so tun, als wären es Argumente“. Rust würde hier in die falsche Richtung gehen. Die Syntax sollte klarer sein, ja, aber nicht verwirrender. Bitte hör auf.

@phaazon
Ich persönlich stimme dem verlinkten Vorschlag voll und ganz zu, dass das Zulassen von Konstanten in der Argumentposition ein wirklich guter ergonomischer Schub sein wird. Es geht nicht nur um Funktionssignaturen (wohl where Klauseln "lösen" das Problem überhaupt nicht, sondern erschweren das Verständnis von Signaturen, da Sie 3 Stellen statt nur einer analysieren müssen), sondern auch um wie diese Funktionen verwendet werden. Vergleichen:

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

Die zweite Zeile ist meiner Meinung nach viel natürlicher als die zweite. Ein weiteres Beispiel wäre die Matrixerstellung: MatrixF32::new(3, 3) vs. MatrixF32::new::<3, 3>() . Ich bin nicht der Meinung, dass diese Funktionalität "sehr verwirrend" sein wird. Für den Benutzer ist es nichts weiter als eine zusätzliche Anforderung für ein Funktionsargument. Wir können bereits const-Argumente über Makros emulieren (siehe SIMD-Intrinsik in std ), aber das ist ziemlich hässlich und ineffizient.

Was impl Trait in der Argumentationsposition angeht, war ich anfangs auch gegen dieses Feature, aber nach einiger Zeit habe ich meine Meinung geändert und glaube, dass es am Ende eine gute Entscheidung war. Der einzige unvollständige Teil ist derzeit die Interaktion mit expliziten Typparametern, die über Turbofish bereitgestellt werden. Ich denke, eine gute Lösung wäre, impl Trait Argumente für Turbofish unsichtbar zu machen, dh Benutzer sollten es verwenden, wenn sie sicher sind, dass keine explizite Typ-Disambiguierung erforderlich ist. Dies würde es uns ermöglichen, in einigen Szenarien den Bedarf an _ in Turbofish erheblich zu reduzieren.

Ich denke, eine Diskussion über const-Argumente ist hier nicht Thema, daher sollte sie hier wahrscheinlich nicht fortgesetzt werden.

Eine Sache, die const Argumente von impl Trait Argumenten (die @PvdBerg1998 erwähnte) unterscheidet, ist, dass es sich um Argumente handelt, mit allem, was für Typrückschlüsse impliziert wird.

Sie können einen Typ selbst nicht als Argument übergeben, aber Sie können einen Wert übergeben, und es ist völlig vernünftig, eine Funktion ihre konstanten generischen Argumente aus ihren normalen Argumenten in Klammern ableiten zu lassen.

Dies ist in C++ ziemlich üblich, zum Beispiel:

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

Dies unterscheidet sich auf subtile Weise davon, einfach einen konstanten Parameter zu akzeptieren und ihn in einen generischen konstanten Parameter zu entzuckern ( fn foo(const N: usize) -> fn foo<const N: usize>() ). Es ist näher, einen Parameter mit einem generischen const-Parameter einzuschränken und dann das generische const-Argument aus dem Argument abzuleiten. Das obige Matrixbeispiel könnte also so aussehen:

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

...wobei usize<X> eine erfundene Syntax für "ein usize mit dem Wert X " ist. (Analog können Sie usize<X..Y> für Bereichstypen verwenden, die im Zusammenhang mit der Verallgemeinerung von NonZero besprochen wurden.)

Wenn Sie Roping in Bereichstypen vermeiden möchten, könnten Sie sich abhängige typisierte Sprachen ansehen, die es ermöglichen, Parameter direkt in Typen zu verwenden, mit ein wenig Anpassung des Bereichs:

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

Sie sind keine Argumente. Sie sind Generika . Auf Typsystemebene bedeutet dies, dass ihre _Werte_ zur Kompilierzeit leben, während die Werte eines Funktionsarguments zur Laufzeit leben. Beides zu mischen ist extrem irreführend.

Da mir Typensysteme wichtig sind, verstößt dieser Vorschlag gegen viele vernünftige und solide Prinzipien. Sie möchten Werte und Typen nicht vermischen. Denn, ja, ein N (beachte, ich habe nicht usize gesagt, ich sagte N ), obwohl du denkst, dass es ein Wert ist, ist viel eher ein Typ als ein Wert. Als Grund hat Haskell keine const-Generika, aber es hat DataKinds , was es ermöglicht, reguläre Datenkonstruktoren als Typen zu heben:

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

So:

fn foo<const X: usize>()

Hier entspricht X eher einem Typ als einem Wert.

Außerdem interessiere ich mich für Monomorphisierung. Wenn ich das lese:

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

Wenn wir uns bei Ihrem Vorschlag die Definition von foo ansehen, ist es schwer zu erkennen, welche Argumente foo monorisieren werden. Denn jedes Mal, wenn Sie einen anderen Wert für ein const generisches übergeben, erstellen Sie eine komplett neue Funktion.

Vielleicht fühlt es sich für _Sie_ und _Ihre Gründe_ intuitiver an, aber ich habe auch Gründe zu sagen, dass es in Bezug auf die Typkorrektheit überhaupt nicht intuitiv ist. Zu sagen, dass es sich um Argumente handelt, ist so, als ob parametrierte Funktionen in der Mathematik ihre Parameterargumente haben. Sie verwechseln Parameter und Argumente und zeigen, wie schlecht diese Idee ist.

Sie haben mich vielleicht falsch verstanden, ich habe ausdrücklich gesagt " const Argumente ... sind Argumente " und nicht " const Generika ". Mein erstes Rust-Beispiel ist auch genau äquivalent zu DataKinds ( usize<N> ist die Version auf Typebene von N ). Hier gibt es kein Problem in Bezug auf die Typkorrektheit - ich argumentiere nicht in Bezug auf die Intuition, sondern in Analogie zu bestehenden, etablierten und soliden Typsystemen.

Was Ihre Bedenken hinsichtlich der Monomorphisierung betrifft - das ist nicht anders als heute! Jedes Argument kann potentiell eine neue Monomorphisierung verursachen. Ich möchte auch hinzufügen, dass die Lösung für dieses Problem darin besteht, die Kosten aller Generika zu senken, indem nicht-abhängigen Code zwischen Instanzen geteilt wird, einschließlich und insbesondere durch Konvertieren von const-Generika in Laufzeitwerte, wenn möglich.

(Und ich bin völlig verwirrt von Ihrer Behauptung, dass ich Parameter und Argumente verwechsle, ich habe sie sorgfältig unterschieden.)

Ich verstehe, was du meinst, aber ich bin immer noch sehr besorgt. Da das _Argument_ mit _const_ gekennzeichnet ist, bedeutet dies, dass Sie es nicht _einen Wert_ übergeben können, was zur Laufzeit spricht. Sie müssen ihm einen _konstanten Wert_ übergeben, der dem _konstanten generischen_ zugeordnet wird. Übrigens, Sie haben es hier nicht erwähnt, aber das ist nur eine Anwendung eines _singleton type_, wenn Sie 10 als Typ betrachten: Sein einziger möglicher Wert ist 10 .

Da ich in der Argumentposition gegen Impl Trait war, bin ich auch dagegen (und Sie werden verstehen, warum wir hier vergleichen können). Mehrere Punkte:

  1. Ich denke nicht, dass es notwendig ist, während _const generics_ viele Werte hinzufügt.
  2. Da sie mit Typen verwandt sind, sollten sie dorthin gehören, wo sich Typen befinden, nicht dorthin, wo sich Variablen befinden.
  3. Ich würde wirklich gerne wissen, warum manche Leute denken, dass <…> schwieriger ist als (…) , insbesondere wenn man bedenkt, dass struct vs. fn . Derzeit hat struct nur <…> und fn hat beides, da auf beide Parameter gesetzt werden können.
  4. Was mir bei impl Trait in arg-Position wirklich nicht gefallen hat, war die Tatsache, dass es schwieriger ist zu sehen, womit eine Funktion wirklich Parameter setzt. Wenn Sie foo<A, B> lesen, wissen Sie, dass es zwei Parameter gibt, die zu einer Funktionsdefinition und -implementierung führen.

Ich spüre, was du hier tun willst. foo(1, 3) fühlt sich für dich besser an als foo<3>(1) , aber nicht für mich. Der Grund dafür ist, dass foo(1, 3) das zweite Argument als laufzeitbasiert akzeptieren sollte und der Vorschlag, den Sie geben, dies verbietet. Ich kann zwar Anwendungen sehen, insbesondere mit Arrays, aber ich mag derzeit nicht, wie es aussieht. Ich wäre viel mehr für eine Lösung, die sagt: „Wenn ein Argument const , können wir eine Typvariable / const generisch ableiten.

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

Das ist nur eine imaginäre Syntax, die ich mir ausgedacht habe, um meinen Standpunkt zu erklären: Wenn Sie b als konstanten Wert übergeben, N = b und Sie erhalten Ihre schöne Syntax:

foo(1, 3)

Wenn b kein konstanter Wert ist, müssen Sie N explizit übergeben:

foo<3>(1, x)

Wie wäre die Syntax dafür mit Ihrem Vorschlag:

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

Es führt uns zu Fragen nach assoziierten Elementen, kurz gesagt, new() ist eine verbundene Funktion und muss mit einem konkreten Typ verbunden sein, konstante generische Elemente haben eine Semantik wie "Dieser Typ hat diesen Parameter...", dh einen abhängigen Typ. Also sollte new() im Kontext keine Argumente haben. Die explizite Verwendung sollte wie folgt aussehen: Type<32>::new() . Und natürlich sollten alle const generischen Argumente eidierbar sein.

Ich spüre, was du hier tun willst. foo(1, 3) fühlt sich für dich besser an als foo<3>(1), aber nicht für mich. Der Grund dafür ist, dass foo(1, 3) das zweite Argument als laufzeitbasiert akzeptieren sollte und der Vorschlag, den Sie machen, dies verbietet.

Das ist weder meine Motivation noch meine Absicht - ich spreche nicht darüber, was sich "besser anfühlt" und ich glaube nicht, dass die Unterscheidung zwischen Kompilieren und Laufzeit mit der Unterscheidung zwischen Typ und Wertebene verschmolzen werden muss. Die beiden sind bereits getrennt - const Elemente (Wert oder Fn) sind sicherlich keine Typen!

Alles, worauf ich hinaus will, ist die semantische Verbindung, die die Typinferenz zwischen Parametern auf Wertebene und Parametern auf Typebene herstellt. Es gibt keine Beziehung zu impl Trait - mein Punkt war wieder const Argumente zu unterscheiden impl Trait , genau diese Art von unproduktiver Angst , den Kopf ab.

Wie wäre die Syntax dafür mit Ihrem Vorschlag:

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

Diese Syntax würde sich nicht ändern! Ich schlage nicht vor, dass wir const-Parameter aus der Typebene entfernen (syntaktisch oder semantisch), sondern nur, dass wir sie der Wertebene hinzufügen, teilweise um die Inferenz der Typebene zu erleichtern.

Auf der cppcon gab es eine Diskussion über constexpr-Funktionsparameter, die dem von @PvdBerg1998 ähnlich zu sein scheint und wie der interne Thread aussieht, mit dem @mark-im verlinkt ist. Es scheint dort gut angekommen zu sein.

Es scheint eine gute Idee zu sein, aber es sollte ausgearbeitet werden, nachdem wir die erste Implementierung für const-Generics abgeschlossen haben

CppCon 2019: David Stone - Entfernen der Metaprogrammierung aus C++, Teil 1 von N: constexpr Function Params

Bearbeiten: Dies wurde in diesem Thread erwähnt, hier ist das zugehörige Papier für constexpr-Funktionsparameter
https://github.com/davidstone/isocpp/blob/master/constexpr-parameters.md

Ich möchte darum bitten, dass wir uns die Diskussion einer solch bedeutenden syntaktischen Änderung entweder für einen Thread zu Interna oder ein Problem (oder eine Änderung der PR) im RFCs-Repo vorbehalten. Der Kommentarbereich dieser Ausgabe ist bereits ziemlich lang und die fragliche Syntax scheint für ein brauchbares MVP von const-Generika unnötig zu sein.

Wenn wir Kommentare verschieben könnten, würden wir das wahrscheinlich tun. Vielleicht möchten wir das Problem für @rust-lang-Teammitglieder sperren und alle Offtopic-Kommentare ausblenden?

Alle Designdiskussionen sollten im RFC-Repository stattfinden und alle Fehlerberichte sollten in separaten Ausgaben enthalten sein.

cc @rust-lang/moderation Gibt es dafür einen Präzedenzfall?

Sie können einen Kommentar in ein Problem _konvertieren_, aber keine Kommentare verschieben. Das Sperren ist in Ordnung, und ich würde Kommentare außerhalb des Themas einfach ausblenden.

Zu Beginn des neuen Jahres dachte ich, es wäre gut, ein weiteres Update darüber zu geben, wo wir jetzt mit const-Generika stehen. Es ist etwas mehr als 2 Jahre her, dass die const Generika RFC akzeptiert wurde. Im ersten Jahr (ungefähr 2018) wurde eine Reihe umfangreicher Refactoring-Anstrengungen unternommen, um const generisches zu erleichtern, indem der Umgang mit generischen Parametern im Allgemeinen im gesamten Compiler verbessert wurde. Im zweiten Jahr (ungefähr 2019) begann die Arbeit an der Implementierung von const-Generika selbst: Zuerst wurde das Nötigste zum Laufen gebracht und dann die Verbesserung der Integrität des Features verlangsamt. Die Leute begannen auch mit der Verwendung von const-Generika im Compiler selbst zu experimentieren. Genauere Beschreibungen dieser Bemühungen finden sich in den ersten beiden Update-Beiträgen: [1] , [2] .


In den letzten Monaten seit dem letzten Update gab es gute Fortschritte.

  • @skinny121 :

    • Es wurde ein Problem behoben, bei dem const-Generika bei inkrementeller Kompilierung und über Kisten hinweg unterbrochen wurden (https://github.com/rust-lang/rust/pull/65652)

    • Fehler mit Typrückschluss behoben, der sich auf const-Generika in der Diagnose auswirkte (https://github.com/rust-lang/rust/pull/65579)

    • Refactoring der Schnittstelle für die const-Auswertung (https://github.com/rust-lang/rust/pull/66877)

  • @yodaldevoid hat die Begriffsklärung von type und const-Parametern implementiert, sodass const-Parameter nicht mehr in {} (https://github.com/rust-lang/rust/pull/66104)
  • @eddyb hat die Verwendung von const-Generika als Array-Längen behoben, sobald die faule Normalisierung implementiert ist (https://github.com/rust-lang/rust/pull/66883)
  • @varkor :

    • Implementierte structural_match Prüfung, um zu verhindern, dass Typen mit benutzerdefinierter Gleichheitsprüfung als const-Generika verwendet werden (https://github.com/rust-lang/rust/pull/65627)

    • Einige Diagnosen korrigiert, um Const-Generika zu berücksichtigen (https://github.com/rust-lang/rust/pull/65614)

    • Verschiedene Refactoring- und Typinferenzkorrekturen durchgeführt (https://github.com/rust-lang/rust/pull/65643, https://github.com/rust-lang/rust/pull/65660, https://github. com/rust-lang/rust/pull/65696)

Ein großes Dankeschön an alle, die mit const Generika geholfen haben!


Was kommt als nächstes? Der größte Blocker für const-Generika ist derzeit die Lazy Normalization , die für bestimmte Arten von const-generischen Grenzen (wie in Arrays ) erforderlich ist. @skinny121 untersucht derzeit die faule Normalisierung und setzt ihre fantastischen Bemühungen fort, die großen Fehler in const-Generika zu beseitigen . Dies würde viele der gegenwärtigen Probleme mit const-Generika lösen.

Abgesehen von der faulen Normalisierung gibt es immer noch eine beträchtliche Anzahl von Fehlern und Papierschnitten, die wir

Gibt es eine Untermenge von const-Generika, die keine faule Normalisierung erreicht und nicht viele Fehler und Papierschnitte aufweist, die aber eine gute Menge an nützlichem Code ausdrücken können? Ich stelle fest, dass die Impls vieler Eigenschaften von std für Arrays gut zu funktionieren scheinen. Vielleicht gibt es eine Einschränkung, die es anderen Crates ermöglicht, die Art von Impls zu schreiben, die wir in std für ihre eigenen Eigenschaften haben, obwohl sie nicht alle ausgefalleneren Funktionen unterstützen?

Wir haben jetzt mehr als die Hälfte des Jahres hinter uns, daher fasse ich kurz die Arbeit zusammen, die im Gange ist. In den letzten 6 Monaten waren viele Leute daran beteiligt, den Support von const-Generika zu verbessern. Vielen Dank also an alle, die in irgendeiner Weise geholfen haben! Ich möchte besonders @skinny121 und @lcnr ​​danken , die beide einige große Funktionen in Angriff genommen haben, die const vermissen : faule Normalisierung für Konstanten und Unterstützung von const-Argumenten in Methodenaufrufen sowie das Beheben zahlreicher schwieriger Fehler. Ihre Bemühungen sind aus der folgenden Zusammenfassung ersichtlich.

Const-Generika wird jetzt an mehreren Stellen im Compiler verwendet, und es gibt bereits Experimente mit Merkmalen und Funktionen, die Const-Generika verwenden, z. B. slice::array_chunks und IntoIterator und FromIterator für [T; N] (https://github.com/rust-lang/rust/pull/65819, https://github.com/rust-lang/rust/pull/69985). Die Beschränkung der Länge 32 für Implementierungen von Array-Eigenschaften wird auch endlich für die Entfernung vorbereitet .

Wir diskutieren derzeit die Stabilisierung einer Teilmenge von const-Generika , die eine breite Palette von Anwendungsfällen abdecken sollten (obwohl es noch einige Probleme zu lösen gibt, bevor const-Generika vollständig stabilisiert werden können).


  • @skinny121 :

    • Implementierung der ersten Version von Lazy Normalization (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)

    • Verbesserte Leistung (https://github.com/rust-lang/rust/pull/68118)

    • Diverse Fehler behoben (https://github.com/rust-lang/rust/pull/68143)

  • @lcnr :

    • Unterstützung für explizite const-Generika in Typargumenten für Methoden hinzugefügt (https://github.com/rust-lang/rust/pull/74113)

    • Die erste Implementierung der Lazy-Normalisierung für Konstanten (https://github.com/rust-lang/rust/pull/71973) mit @skinny121 abgeschlossen

    • Fussel für unused_braces hinzugefügt (https://github.com/rust-lang/rust/pull/70081)

    • Problem mit const-Generika in Mustern behoben (https://github.com/rust-lang/rust/pull/70562)

    • dyn Trait für const generische Parameter verboten (https://github.com/rust-lang/rust/pull/71038)

    • Verbessertes Pretty-Printing (https://github.com/rust-lang/rust/pull/72052)

    • Diverse Fehler behoben (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)

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

    • Tests hinzugefügt (https://github.com/rust-lang/rust/pull/74392, https://github.com/rust-lang/rust/pull/74445)

  • @eddyb :

    • Probleme mit const-Generika in Array-Längen behoben (https://github.com/rust-lang/rust/pull/70452)

    • Verbessertes Pretty-Printing (https://github.com/rust-lang/rust/pull/71232)

    • Verbesserte Fehlerbehandlung (https://github.com/rust-lang/rust/pull/71049)

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

  • @ Aaron1011 : Fehler bei der Verwendung von Cross-
  • @petrochenkov : Probleme mit Namensauflösung und const-Generika behoben (https://github.com/rust-lang/rust/pull/70006)
  • @yodaldevoid : Ein Problem mit der lebenslangen Nutzung in const generischen Parametern wurde behoben (https://github.com/rust-lang/rust/pull/74051)
  • @contrun : ICE behoben (https://github.com/rust-lang/rust/pull/69859)
  • @oli-obk: Refactoring (https://github.com/rust-lang/rust/pull/69981)
  • @Centril : verbesserte Diagnose (https://github.com/rust-lang/rust/pull/70261)
  • @DutchGhost : Test hinzugefügt (https://github.com/rust-lang/rust/pull/70539)
  • @varkor :

    • Verbessertes Pretty-Printing (https://github.com/rust-lang/rust/pull/67909, https://github.com/rust-lang/rust/pull/68111)

    • Diverse Fehler behoben (https://github.com/rust-lang/rust/pull/67906, https://github.com/rust-lang/rust/pull/68388, https://github.com/rust- lang/rost/ziehen/68434)

    • Verbesserte Diagnose (https://github.com/rust-lang/rust/pull/70845)

    • Tests hinzugefügt (https://github.com/rust-lang/rust/pull/68312, https://github.com/rust-lang/rust/pull/70939)


Obwohl Const Generics bereits einen langen Weg zurückgelegt hat, gibt es immer noch Bugs und scharfe Kanten . Wenn Sie daran interessiert sind, eines der verbleibenden Probleme anzugehen , können @lcnr oder @eddyb für das Problem oder Zulip anpingen, um

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen