Rust: Tracking-Problem für "Macros 1.1" (RFC # 1681)

Erstellt am 22. Aug. 2016  ·  268Kommentare  ·  Quelle: rust-lang/rust

Tracking-Problem für rust-lang / rfcs # 1681.

cc @alexcrichton

Stabilisierung TODO

Lackmustests:

Eigenschaften:

  • Der Name der Kiste lautet derzeit proc_macro
  • Kistentyp, derzeit proc-macro
  • Das Attribut #[proc_macro_derive(Foo)]
  • Laden von proc-macro Kisten mit -L und #[macro_use] , um sie zu laden
  • Das Abschatten ist ein Fehler
  • keine Hygiene
  • Übergeben eines Token-Streams für die gesamte Struktur und Zurückerhalten aller Daten
  • Frachtmanifestattribut, derzeit proc-macro = true

Bekannte Fehler:

  • [] - Panikableitung hat möglicherweise die falsche Spanne - # 36935
  • [] - Token-Streams mit mod foo fail - # 36691
  • [] - Dokumente werden nicht für proc_macro - # 38749 veröffentlicht
  • [x] - Benutzerdefinierte Attribute für mehrere Modi sind schwierig - https://github.com/rust-lang/rust/issues/35900#issuecomment -252499766
  • [x] - Frachttest schlägt für proc macro libs fehl - # 37480
  • [x] - Bestellung ist immer noch wichtig - https://github.com/rust-lang/rust/issues/35900#issuecomment -252430957 (fixiert von https://github.com/rust-lang/rust/pull/37067)
  • [x] - Rustc-Makro-Kisten können nicht dokumentiert werden - https://github.com/rust-lang/rust/issues/36820 (behoben in # 36847)
  • [x] - Fracht wird zu oft neu aufgebaut - https://github.com/rust-lang/rust/issues/36625 (behoben in https://github.com/rust-lang/rust/pull/36776)
  • [x] - Vom Compiler generierte Attribute erschweren das Leben von Autoren aus benutzerdefinierten Ableitungen - https://github.com/rust-lang/rust/issues/35900#issuecomment -245978831

  • [x] - Erstelle eine rustc_macro Kiste

    • [x] - Lassen Sie librustc_macro mit libsyntax verknüpfen. Hängen Sie von librustc_macro in librustc_driver
    • [x] - Kennzeichnen Sie rustc_macro mit unserem Standardheader als instabil.
    • [x] - Kennzeichnen Sie rustc_macro mit #![crate_type = "rlib"] , und produzieren Sie keine Dylib.
    • [x] - Implementieren Sie die API von rustc_macro mithilfe von libsyntax 's TokenStream intern
    • [x] - Kennzeichnen Sie rustc_macro mit einem TokenStream lang-Element, damit der Compiler davon erfährt.
  • [x] - rustc_macro_derive Attribut hinzufügen

    • [x] - Überprüfen Sie, ob es genau die Form foo(bar) , keine anderen Argumente / Formate

    • [x] - Vergewissern Sie sich, dass es nur auf Funktionen angewendet wird

    • [x] - Vergewissern Sie sich, dass es nur auf Funktionen im Root-Modul angewendet wird

    • [x] - Überprüfen Sie die Signatur mit dem oben hinzugefügten Element TokenStream lang

    • [x] - Codieren Sie alle Funktionssymbole mit rustc_macro_derive zusammen mit dem Ableitungsmodus, für den sie verwendet werden, in Metadaten.

  • [x] - Fügen Sie einen Kistentyp rustc-macro für andere Kisten hinzu

    • [x] - verdrahten Sie es, um eine Dylib zu erzeugen

    • [x] - Stellen Sie sicher, dass die Dylib Metadaten erhält

    • [x] - Stellen Sie sicher, dass rustc-macro Kisten nicht als Dylibs verknüpft werden können

    • [x] - Stellen Sie sicher, dass keine anderen erreichbaren Elemente als die mit #[rustc_macro_derive]

    • [x] - Fügen Sie cfg(rustc_macro) als instabile cfg Direktive hinzu und legen Sie sie für den rustc-macro

    • [x] - Stellen Sie sicher, dass rustc-macro Kisten dynamisch mit libsytnax verknüpft sind

  • [x] - Füllen Sie #[macro_use] Unterstützung für rustc-macro Kisten aus

    • [x] - Erweitern Sie den Bibliothekslader, um rustc-macro Kisten getrennt von Dylib / Rlib zu finden, die heute beim Lodieren von Kisten verfolgt werden

    • [x] - Analysieren Sie Metadaten für rustc-macro Kisten, um mehr über Symbol / Ableitungsmodus-Paarungen zu erfahren

    • [x] - dlopen die Dylib

    • [x] - Generiere einen Fehler, wenn ein Ableitungsmodus einen anderen beschatten würde.

  • [x] - Fracht-Ganzzahl hinzufügen

    • [x] - erkenne rustc-macro ähnlich wie plugin = true

    • [x] - Übergeben Sie --crate-type=rustc-macro wenn Sie davon abhängig sind

    • [x] - Für Host-Kisten dieselbe Host- / Ziellogik wie für Plugin-Kisten (z. B. immer Rustc-Makro-Kisten für Hosts kompilieren).

  • [x] - Tests

    • [x] - Rauchtest beim Laden eines Rustc-Makros, Dummy-Merkmal #[derive]

    • [x] - Namenskonflikte sind ein Fehler

    • [x] - Das Kompilieren für die falsche Architektur ist ein Fehler

    • [x] - Kustentyp rustc-macro nicht ausgeben und alles andere (z. B. rustc-macro + dylib) ist ein Fehler

    • [x] - Informationen, die nicht schrecklich sind

    • [x] - Entfernen von Attributen aus einer Struktur sowie von Feldern

    • [x] - Hinzufügen von Impls neben einer Struktur

    • [x] - Die Kreuzkompilierung sucht nach dem Host-Rustc-Makro-Kistentyp

    • [x] - Laden Sie Vanille-Dylibs nicht als Rustc-Makro-Kistentypen

    • ]

    • [x] - Ableiten von Makros muss eine Signatur erfordern

    • [x] - Laden Sie zwei Makrokisten in eine Zusammenstellung

B-RFC-implemented B-unstable T-lang final-comment-period

Hilfreichster Kommentar

Ok, ich werde das heute untersuchen und sehen, wie weit ich komme.

Alle 268 Kommentare

Ich habe die Problembeschreibung mit einer Checkliste aktualisiert, die zeigt, was zu tun ist. Es ist wahrscheinlich nicht erschöpfend, aber es sollte uns hoffentlich 90% des Weges dorthin bringen

Ok, ich werde das heute untersuchen und sehen, wie weit ich komme.

Ab # 35957: Wir sollten den Namen der Librustc_Macro-Kiste noch weiter radeln. Insbesondere soll dies eine langlebige Kiste sein, die für alle Makroautoren das Wesentliche enthält. Daher scheint es schlecht, sich auf rustc_macro zu beschränken (was zumindest in meinen Augen so ist), dass die Idee 1.1 schlecht ist. Ich wollte vorher libmacro dafür, aber da macro ein reserviertes Wort ist (und wir es vielleicht in Zukunft als Schlüsselwort wollen), ist das unmöglich. @cgswords und ich haben libproc_macro verwendet, und ich denke, das ist kein schlechter Name, obwohl ich damit nicht 100% zufrieden bin.

@nrc : Okay, meine unmittelbaren Gedanken zur Benennung:

  • extern crate macros; - kurz und bündig, kann aber so gelesen werden, dass es Makros enthält, anstatt Code zum Schreiben zu unterstützen
  • extern crate macro_runtime; - genau das, was es verspricht
  • extern crate metarust; - schreibe Rust über Rust, um mit Rust zu arbeiten
  • extern crate bikeshed; - Mit prozeduralen Makros können Sie jede gewünschte Rostfarbe haben!
  • extern crate macrame; - klingt wie "macro make [r]"; möglicherweise besser für eine zukünftige "nette" API über die Rohbibliothek gelassen.

@nrc Ein wichtiger Aspekt dieser Frage scheint die Benennung unserer verschiedenen libproc_macro , verpflichten wir uns hart zum "prozeduralen Makro". Terminologie. Ich habe hier keine feste Meinung, aber ich bin mir nicht sicher, ob wir den Raum der Terminologie offen untersucht haben.

Denken Sie, um klar zu sein, dass das heutige macro_rules einfach "Makros" ist, dh die Standardeinstellung, die wir unter Makros verstehen, während Sie "prozedurale Makros" qualifizieren müssen? Das scheint mir ein ziemlich vernünftiger Plan zu sein. Und in dieser Welt würde ich argumentieren, dass libproc_macro besser ist als libmacros .

Ich denke hier, dass alle Makros "Makros" sind, und wenn wir anhand der Implementierung unterscheiden müssen (die Verwendung sollte für alle Arten genau gleich sein), verwenden wir "prozedurale Makros" und "Makros anhand von Beispielen". Ich möchte "Syntaxerweiterung" und "Compiler-Plugin" vollständig verbannen (und eines Tages letzteres für tatsächliche Plugins wiederverwenden).

Aber ja, ich möchte unbedingt hinter die Terminologie "prozedurales Makro" treten.

@nrc Macht für mich Sinn! Während "Makros am Beispiel" etwas unhandlich ist, ist es auch ein sehr eindrucksvoller / intuitiver Begriff. Meine einzige Sorge über "prozedurales Makro" ist, dass "Prozedur" in Rust keine Sache ist. Ich frage mich, ob es eine Möglichkeit gibt, die Verbindung direkter herzustellen. "Funktionsmakro" ist nicht ganz richtig, aber gibt Ihnen vielleicht einen Eindruck davon, was ich meine?

Ja, es ist nicht ganz perfekt, aber da es ein bekannter / verwendeter Begriff außerhalb von Rust ist, denke ich, dass es besser ist, als unseren eigenen Begriff zu prägen. "Programmatisches Makro" ist möglich, aber ich bevorzuge "prozedural".

Perls Begriff für das nächste Äquivalent zu "prozeduralen Makros" ist "Quellfilter", was (insbesondere bei der Umstellung von AST auf Token) eine ziemlich zutreffende Beschreibung ist.

Vielleicht würden 'Syntaxtransformatoren' oder 'programmatische Makros' als Namen gut funktionieren? Ich habe jedoch kein Problem mit prozeduralen Makros.

"Prozedural" ist bereits das, was die Leute dies nennen, und ich denke, es ist klar verstanden, was es bedeutet, insbesondere im Gegensatz zu "Makros mit gutem Beispiel". Ich würde mir keine Sorgen machen, einen anderen Namen zu finden.

Ich mag den Begriff "prozedurales Makro" für den regulären Gebrauch (oder vielleicht "benutzerdefiniertes Makro"). Ich mag es besonders, das Wort _macro_ zu verwenden, damit klar ist, dass sie (irgendwann ...) auf die gleiche Weise wie "normale Makros" verwendet werden können. Aus diesem Grund mag ich "Quellfilter" nicht (ich erwarte auch, dass ein Filter nur Daten löscht, nicht transformiert, obwohl ich weiß, dass der Begriff für beide verwendet wird).

Mir geht es gut mit entweder libproc_macro oder libmacros . Ich bevorzuge letzteres, nur weil ich es nicht mag, _ in Kisten-Namen zu haben, wenn es leicht zu vermeiden ist. =)

Eine Frage: Erwarten wir jemals "Support-Routinen", die von nicht prozeduralen Makros verwendet werden können? Ich kenne keine solchen Pläne, aber wenn wir das tun und sie in derselben Kiste haben wollen, dann wäre libmacros ein besserer Name. =)

(Ich denke ein wenig über zB den Kommentar von

@nikomatsakis : Eine verwandte, aber subtil andere Frage ist ein Anwendungsfall, den ich in https://github.com/rust-lang/rfcs/pull/1561#discussion_r60459479 angesprochen habe. Möchten wir, dass die Implementierungsfunktionen von prozeduralen Makros aufgerufen werden können? Implementierungsfunktionen anderer prozeduraler Makros?

Ich kann leicht erkennen, dass ich möchte, dass eine benutzerdefinierte Ableitung eine andere aufruft, und das würde im Wesentlichen dazu führen, dass prozedurale Makrodefinitionen selbst als solche "Unterstützungsroutinen" verwendet werden können.

Aber ja, ich denke, @dhermans gensym Beispiel ist ziemlich überzeugend. Wenn die Antwort auf meine Frage oben "Ja" lautet, ist Gensym natürlich sowohl ein Makro (das von Makros als Beispiel verwendet werden kann) als auch eine Unterstützungsfunktion (die von prozeduralen Makros verwendet werden kann).

Ich habe eine Fracht-PR https://github.com/rust-lang/cargo/pull/3064, die alle Kontrollkästchen "Frachtintegration" in der Checkliste aktivieren sollte.

Ich habe einen Kommentar zur Fracht-PR hinterlassen, aber ich denke, wir wollen eine andere Art von Abhängigkeit, nicht nur eine andere Art von Paket. Erstens denke ich, dass dies nur ästhetisch und ergnomisch besser ist, aber das ist nur meine Meinung. Aber ich habe auch zwei konkrete Gründe.

  • In einer Zukunft mit öffentlichen und privaten Vertretern ist es wichtig zu wissen, dass öffentliche Vertretungen von Verfahrens- und regulären Vertretern nicht zusammenfallen müssen. Z.B
  • In einer Zukunft mit Inline-Prozedur-Makros / Quasi-Quoting kann jede Bibliothek zur Kompilierungszeit verwendet werden.
  • > Wollen wir, dass die Implementierungsfunktionen von prozeduralen Makros die Implementierungsfunktionen anderer prozeduraler Makros aufrufen können?

Wie dies zeigt, kann dieselbe Kiste eine reguläre Dep einer anderen Kiste mit prozeduralen Makros oder eine Makro-Dep einer anderen Kiste sein.

Funktioniert serde?

Ja https://github.com/serde-rs/serde/releases/tag/v0.8.6

(mit Ausnahme der Containerattribute in einigen Fällen # 36211)

Super, danke für die Updates @dtolnay!

Gibt es Dokumente zu diesen Makros? Ich nehme an, das einzige Beispiel für ihre Verwendung ist serde, habe ich recht?

OK Frachtzeug ist gelandet. Das ist in Ordnung, aber es wäre schön, https://github.com/rust-lang/rust/issues/35900#issuecomment -243976887 irgendwann vor der Stabilisierung erneut zu besuchen. [Für was es wert ist, ich wollte dies im ursprünglichen RFC ansprechen, habe es aber vergessen.]

Ich denke, "Makros durch Beispiel" und "prozedurale Makros" könnten besser als "deklarative Makros" bzw. "imperative Makros" kategorisiert werden. Dies gibt informative Parallelen zur bekannteren deklarativen / imperativen Kategorisierung von Programmiersprachen. Da der Imperativ als Obermenge oder Synonym für prozedural behandelt wird, sollte er nah genug sein, damit Personen, die an die Terminologie "prozedurales Makro" gewöhnt sind, den Sprung machen können. Es sollte auch jegliche Verwechslung mit Verfahrens- / Funktions- / Methodenkonzepten im Rost selbst vermieden werden.
Dieses Namensschema gibt uns eine macro_imp Kiste und ein Modul, die parallel zu macro_rules . macro_rules könnte schließlich ein Modul einer allgemeineren macro_dec Kiste werden.

@nrc , Wenn Sie sich auf "aktuelle Plugins" beziehen, schließen Sie Dinge wie metacollect und clippy , Dinge wie rustw , rustfmt und den Rust Language Server oder eine andere Programmkategorie ein?

Für das, was es wert ist, mag ich den Namen "mit gutem Beispiel" leicht nicht (da $foo Muster in meinem Kopf keine "Beispiele" sind). Deklarativ gegen Imperativ klingt für mich besser.

Beim Herumspielen habe ich ein Problem bemerkt. Es sieht so aus, als ob die von Rust bereitgestellten Ableitungen manchmal Attribute hinzufügen, die der Compiler ignorieren kann. Dieser Kontext geht verloren, wenn er eine benutzerdefinierte Ableitung durchläuft. Ich würde erwarten, dass die als benutzerdefinierte Ableitung angegebene Identifizierungsfunktion ein No-Op ist, aber es wird zu Fehlern beim Hinzufügen des Attributs #[structural_match] kommen.


Reproduktionsskript

(In einer Kiste mit dem Namen demo_plugin )

#![feature(rustc_macro, rustc_macro_lib)]

extern crate rustc_macro;

use rustc_macro::TokenStream;

#[rustc_macro_derive(Foo)]
pub fn derive_foo(input: TokenStream) -> TokenStream {
    input
}

(in einer anderen Kiste)

#![feature(rustc_macro)]

#[macro_use] extern crate demo_plugin;

#[derive(PartialEq, Eq, Foo)]
struct Bar {
    a: i32,
    b: i32,
}

Durch Entfernen von #[derive(Eq)] funktioniert alles einwandfrei.

@sgrif ah ja in der Tat danke, dass du mich daran erinnert hast!

Hier sind also ein paar Dinge los:

  • Mit #[derive(PartialEq, Eq)] fügt der Compiler stillschweigend #[structural_match] da er statisch versteht, dass die Ableitung von PartialEq diesen Vertrag tatsächlich erfüllt.
  • Ich glaube, ein ähnliches Attribut entsteht, wenn #[derive(Copy, Clone)] mit #[rustc_copy_clone_marker] hinzugefügt wird.
  • Derzeit funktioniert dies, da in der Spanne dieser Attribute "Interne Instabilität zulassen" angegeben ist. Wir verlieren jedoch die Span-Informationen beim Parsen und Reparsen.

Einige Lösungen könnten wir also tun:

  • Konventionell sagen, dass benutzerdefinierte Ableitung an erster Stelle steht, z. B. #[derive(Foo, Eq, PartialEq)]
  • Verlassen Sie sich auf benutzerdefinierte Ableitungen, um diese Attribute wegzulassen
  • Geben Sie keine benutzerdefinierten Attribute aus, wenn wir benutzerdefinierte Ableitungen erkennen
  • Verwenden Sie einen völlig anderen Mechanismus im Compiler, um zu kommunizieren, was diese Attribute sagen, jedoch nicht über den AST selbst.

Ich würde es befürworten, diese Attribute entweder nicht auszugeben oder einen anderen Mechanismus zu verwenden. Dies ist jedoch sehr schwierig, da #[derive(Copy, Foo, Clone)] ebenfalls funktionieren muss (wo benutzerdefinierter Code in der Mitte ausgeführt wird, der Definitionen ändern könnte).

Meine bevorzugte Vorgehensweise wäre, diese Attribute einfach nicht auszugeben, wenn wir benutzerdefinierte Ableitungen erkennen. Auch das mag nicht trivial sein. Im Moment sollte eine Konvention von "Custom First und Standard Last" ausreichen, aber ich denke, wir sollten dies vor der Stabilisierung beheben.

Haftungsausschluss: Ich habe nur eine Außenansicht des Compilers - es gibt also eine Menge, die ich nicht kenne. ^^

Soweit ich weiß, ähnelt der derzeitige Ansatz der benutzerdefinierten Ableitung von Makros 1.1 eher einer vorübergehenden Problemumgehung. Temporäre Problemumgehung kann zu "einer Weile" führen. Aber auf lange Sicht (= Makros 2.0) werden wir den String-Parsing-Roundtrip nicht mehr durchführen, was derzeit zum Verlust von Span-Informationen führt. Ich frage mich, ob diese versteckten Attribute im AST in einer Makros 2.0-Welt so schlecht waren. Vielleicht kann jemand mit mehr Insiderwissen über den Compiler sagen. Wenn diese verborgenen Attribute in dieser zukünftigen Welt tatsächlich Sinn machen, würde ich argumentieren, die Problemumgehung zu versuchen, indem herkömmliche benutzerdefinierte Ableitungen in den Vordergrund gestellt werden. Das Ganze ist sowieso schon eine Problemumgehung, nicht wahr?

@ colin-kiegel Ich glaube, Sie haben Recht in dem Sinne, dass das, was wir gerade tun, nicht zukunftssicher ist. Angenommen, Sie haben zum Beispiel:

#[derive(Eq, Foo, PartialEq)]

Heute würden wir die Implementierung Eq hinzufügen, dann den benutzerdefinierten Code für Foo ausführen und dann eine Implementierung von PartialEq hinzufügen. Die Struktur könnte zwischen _change_ Eq und PartialEq , so dass die #[structural_match] hinzugefügt von Eq kann eigentlich nicht richtig sein , nachdem Foo läuft.

In diesem Sinne stimme ich zu, dass sie auf keinen Fall unbedingt zukunftssicher sind!

Mein Gefühl ist, dass benutzerdefinierte Ableitungen, die auf strukturellen Änderungen beruhen, im Allgemeinen nicht sehr gut komponieren, unabhängig von diesen verborgenen Attributen. Kann ein benutzerdefiniertes v2.0-Derivat die Struktur des Elements ändern oder ist es irgendwie nur auf das Dekorieren beschränkt?

Ja, ich glaube, die Fähigkeit wird immer vorhanden sein (inhärent in der TokenStream -> TokenStream-Schnittstelle), obwohl ich vermute, dass eine vernünftige Implementierung von #[derive] die Struktur der ursprünglichen Struktur beibehalten würde.

Ich nehme nicht an, dass wir verlangen könnten, dass der Ausgabe-Tokenstream nicht die Struktur selbst enthält, um sicherzustellen, dass die Struktur nicht geändert wird. Die Eingabestruktur TokenStream wäre ein unveränderliches Präfix? Das große Problem wäre, sicherzustellen, dass die nicht erkannten Attribute, die die Plugins nach Abschluss des Builds verwenden, ignoriert werden. Vielleicht kann jedes # [derive ()] ein Präfix haben (sagen wir # [derive (Foo)] hat das Präfix Foo_), mit dem die Attribute, die sie verstehen, beginnen müssen, und nach der Verarbeitung jedes benutzerdefinierten Derivats entfernen wir diese Attribute?

@mystor Ja, das Problem bei diesem Ansatz sind nicht erkannte Attribute, weshalb wir die gesamte Struktur als Eingabe haben. Das ist im Allgemeinen flexibler, als sich auf ein bestimmtes Präfix / Suffix / Registrierung / etc. Zu verlassen.

Wenn eine benutzerdefinierte Ableitung der Version 2.0 benutzerdefinierte Attribute als _used_ markieren könnte, könnte sie auf den Nur-Lese-Zugriff auf den Rest des Element-Token-Streams beschränkt sein. Auf diese Weise könnte IMO eine bessere Zusammensetzbarkeit von benutzerdefinierten Ableitungen garantiert werden. Wenn ein v2.0-Makro die Struktur eines Elements ändern muss, muss es eine andere API verwenden, jedoch keine benutzerdefinierte Ableitung. Auf diese Weise würde das Problem mit #[structural_mach] und der Reihenfolge von (benutzerdefinierten) Ableitungen nur in Makros 1.1 auftreten. Wäre das sinnvoll?

Ein anderes Problem. Wenn eine Struktur zwei verschiedene benutzerdefinierte Ableitungen enthält und die zweite in Panik gerät, zeigt die Fehlerspanne auf die erste und nicht auf diejenige, die in Panik geraten ist.


Reproduktionsskript

In einer Kiste namens demo_plugin

#![feature(rustc_macro, rustc_macro_lib)]

extern crate rustc_macro;

use rustc_macro::TokenStream;

#[rustc_macro_derive(Foo)]
pub fn derive_foo(input: TokenStream) -> TokenStream {
    input
}

#[rustc_macro_derive(Bar)]
pub fn derive_bar(input: TokenStream) -> TokenStream {
    panic!("lolnope");
}

In einer anderen Kiste

#![feature(rustc_macro)]

#[macro_use] extern crate demo_plugin;

#[derive(Foo, Bar)]
struct Baz {
    a: i32,
    b: i32,
}

Der Fehler hebt Foo , obwohl Bar Panik geriet.

Danke für den Bericht @sgrif! Ich habe die Beschreibung dieses Problems aktualisiert und hoffe, auch dort alle noch offenen Probleme im Zusammenhang mit Makros 1.1 im Auge zu behalten.

Hmm, die Interaktion von benutzerdefinierten Ableitungen mit #[structural_eq] ist etwas, woran ich vorher nicht gedacht hatte. Es stört mich eher!

Es scheint mir, dass eine Möglichkeit, Text an einen Token-Stream anzuhängen, am Ende des Tages eine bessere Schnittstelle sein könnte ... es würde Span-Informationen bewahren und dieses Problem vermeiden, nicht wahr?

Ein Vorteil der allgemeineren Schnittstelle besteht darin, dass Pakete Attribute für Felder haben können, die beim Ausführen des Makros entfernt werden können. Dies ist der einzige mir bekannte Anwendungsfall, bei dem benutzerdefinierte Ableitungsmakros das ursprüngliche Element ändern können.

Ich denke, dass die Frage der Benutzeroberfläche darin besteht, ob die benutzerdefinierte Ableitung "nur ein anderes Makro" sein soll. In diesem Fall scheint es wichtig zu sein, dieselbe Schnittstelle wie andere prozedurale Makros zu haben (bei denen wir das ursprüngliche Element ändern möchten). Oder ob es sich um eine eigene Sache mit einer speziellen Schnittstelle handeln soll. In diesem Fall ist die restriktivere (Anhängen) Schnittstelle sinnvoll.

Ich werde bemerken, dass Syntaxerweiterungen lange Zeit zwischen Modifikatoren und Dekoratoren unterschieden haben, und ich denke, dass alle Beteiligten diese Unterscheidung wirklich hassten. Ich zögere daher ein bisschen, den Weg zu beschreiten, dass benutzerdefinierte Ableitungen etwas Besonderes sind (eine Alternative, die diskutiert wurde, ist eine ganz spezielle benutzerdefinierte Ableitung, möglicherweise sogar ein deklaratives Format).

@nrc Nun , es kann immer noch Tokenstream sein -> Tokenstream, wo wir einfach keine new -Methode verfügbar machen, die von vorne anfängt, oder?

Ich bin damit einverstanden, dass es möglich sein muss, benutzerdefinierte Attribute für Felder zu haben, damit eine benutzerdefinierte Ableitung wirklich Sinn macht. Aber ich glaube, es gibt viele Möglichkeiten, wie dies mit dem "Append" -Stil gemacht werden kann - dies sollte natürlich diskutiert werden. Ich würde definitiv den Append-Stil bevorzugen, da ich eine Welt bevorzuge, in der abgeleitete Makros das Element nicht ändern und zusammensetzbar sind. Außerdem löst sie das Problem #[structural_eq] . Dies ist genau die richtige Menge an Freiheit IMO.

Wenn die Leute diese Bestimmung nicht mochten, würde ich gerne fragen, warum, weil es offensichtlich genug Gründe gab, diese Unterscheidung vorher zu treffen, nicht wahr?

(Ich denke, was ich vorgeschlagen habe, ist nur ein vorübergehender Hack, der das längerfristige Problem der Zusammensetzbarkeit nicht wirklich angeht.)

Meine verschiedenen Bibliotheken haben derzeit Makros, die wie foo!(Bar, parameters...) aussehen und aus den Parametern eine Struktur Bar generieren.

Während einer Diskussion über IRC hatten wir die Idee, stattdessen #[derive(Foo)] #[params] struct Bar; zu schreiben und foo! durch #[derive(Foo)] ersetzen, die den Hauptteil der Struktur erzeugen würden.

Es ist offensichtlich kein starkes Argument, aber ich mochte diese Idee wirklich, da es für den Benutzer klarer ist, dass eine Struktur erstellt wird.

Ich frage mich, ob wir die #[structural_match] überarbeiten könnten, die stattdessen auf dem generierten Impl platziert werden sollen.

(Löst das Problem nicht wirklich)

Es ist erwähnenswert, dass sowohl Serde als auch Diesel häufig benutzerdefinierte Attribute für Felder verwenden, sodass eine benutzerdefinierte Ableitung unbedingt erforderlich ist, um einen Austausch zu ermöglichen.

Vielleicht denke ich gerade nicht direkt über das Problem #[structural_match] . Was kann ein benutzerdefiniertes Ableiten tun?

  • Wenn die benutzerdefinierte Ableitung das Attribut #[structural_match] fälschlicherweise einfügt, sollte die Stabilitätsprüfung fehlschlagen. Wenn nicht, scheint das selbst ein Fehler zu sein! (Angesichts der komplexen und verrückten Funktionsweise dieses Codes würde mich das nicht überraschen.)
  • Wenn das benutzerdefinierte Ableiten es entfernt, wird kein Schaden angerichtet.

Es tut mir leid, dass ich unzählige kleine Kommentare geschrieben und laut nachgedacht habe, aber es gibt noch ein weiteres Problem. Obwohl eine benutzerdefinierte Ableitung möglicherweise nicht in der Lage ist, eine #[structural_match] -Anmerkung zu "verfälschen" (da sie ohne die "magische Spanne" enden würde), würde sie wahrscheinlich die Spanne einer vorhandenen Annotation vermasseln, es sei denn, die Ableitungen werden in der richtigen Reihenfolge angewendet, was bedauerlich ist. Grundsätzlich ein Beispiel für die Nichtzusammensetzbarkeit, von der @ colin-kiegel gesprochen hat, ohne jedoch zu versuchen, die Struktur im Flug zu ändern.

(Mit anderen Worten, da wir uns auf die Spanne verlassen, um zu beurteilen, ob stabiles Material verwendet werden kann, kann der Verlust von Span-Informationen dort einige knifflige Probleme verursachen.)

EDIT: OK, beim Zurücklesen sehe ich, dass ich gerade das, was

Es ist auch ein bisschen eklig, weil es bedeutet, dass wir instabile Implementierungsdetails stabilem Code aussetzen. Idealerweise würde stabiler Code niemals wissen, dass die Annotation #[structural_match] vorhanden ist.

@nikomatsakis gut, auf die eine oder andere Weise müssen wir verschiedene Einschränkungen erzwingen, je nachdem, ob das Makro eine benutzerdefinierte Ableitung oder eine andere Art sein soll. Dies bedeutet eine separate Behandlung (und unterschiedliche Semantik), unabhängig von der Signatur der Funktion.

@ colin-kiegel

Ich würde definitiv den Append-Stil bevorzugen, da ich eine Welt bevorzuge, in der abgeleitete Makros das Element nicht ändern und zusammensetzbar sind. Außerdem löst es das Problem # [strukturelle_eq]. Dies ist genau die richtige Menge an Freiheit IMO.

Ich denke, dass mutierende Makros zusammensetzbar sein können, obwohl die Bedingungen für die Zusammensetzbarkeit natürlich unterschiedlich sind. Es muss eindeutig eine Reihe von Vor- und Nachbedingungen für den Betrieb von Derivaten geben, die entweder vom Compiler oder durch Konvention durchgesetzt werden. Die Nichtmutation scheint ein Extrem im Spektrum der Invarianten zu sein, die wir hier auswählen könnten, und beachten Sie, dass wir bereits Möglichkeiten diskutieren, wie dies gemildert werden kann (z. B. verwendete Markierungsattribute). Ich denke, im Allgemeinen würden wir die einfachsten Bedingungen bevorzugen und sie vom Compiler durchsetzen lassen. Dies wird jedoch in gewisser Weise durch die Frage zusammengefasst, wie speziell kundenspezifische Ableitungen behandelt werden sollten.

Wenn die Leute diese Bestimmung nicht mochten, würde ich gerne fragen, warum, weil es offensichtlich genug Gründe gab, diese Unterscheidung vorher zu treffen, nicht wahr?

Ich glaube nicht, dass es zu dieser Zeit eine starke Motivation gab. Ich denke, es war einfach zu implementieren und schien eine gute Idee zu sein. Es wurde nicht gemocht, da es die Implementierung komplexer macht, eine Unterscheidung für Makroautoren hinzufügt, die normalerweise irrelevant ist, und Makros weniger flexibel macht, indem sie entscheiden müssen, ob sie geändert oder dekoriert werden sollen.

Ich würde sehr gerne das langfristige Design von kundenspezifischen Ableitungen in Betracht ziehen und sicherstellen, dass wir in die richtige Richtung gehen. Es scheint mir, dass die Einschränkungen der 1.1-Lösung und der Wunsch, so schnell wie möglich so viel wie möglich zu tun, das Wasser hier trüben und wir die größere Vision aus den Augen verlieren.

Ich stimme @jimmycuadra darin zu, dass es anscheinend @nikomatsakis hat auch Recht , als die derzeitige Behandlung von #[derive(PartialEq, Eq)] unterdurchschnittlich ist und wir sie nicht stabilisieren sollten. Schließlich hat @mystor einen sehr guten Punkt, dass benutzerdefinierte Ableitungsmodi nicht einmal über dieses magische Attribut Bescheid wissen sollten. Wir werden in Zukunft sicherlich noch mehr hinzufügen wollen, und ich möchte nicht, dass Makros 1.1 uns daran hindern.

Auch widerhallenden @nrc ‚s Stimmung über die langfristige Gestaltung von kundenspezifischen derive, ich glaube , eine Menge davon läuft darauf hinaus, wie #[derive] tatsächlich funktioniert. Wenn und wann wir beliebige Attribute unterstützen, hat nach einen guten Sinn darin, nur Modifikatoren und keine Dekoratoren zu haben, aber #[derive] ist etwas ganz Besonderes, wenn eine benutzerdefinierte Ableitung kein neues Attribut definiert , sondern nur daran festhält eine bestehende.

Im Moment hat die Implementierung eine strikte Erweiterung der #[derive] -Modi von links nach rechts. Alle internen Ableitungsmodi werden in einer Schleife erweitert. Wenn ein benutzerdefinierter Ableitungsmodus aktiviert wird, werden sie erneut initialisiert, erweitert und kehren dann zu Stufe 1 zurück. Dies bedeutet wiederum, dass der Compiler das Attribut #[derive] _multiple mehrmals ausführen kann Geben Sie definition_ ein. Das führt zu einer Menge Haarigkeit.

Ein Vorschlag, den ich haben könnte, ist, die Erweiterungsreihenfolge von #[derive] zu optimieren:

  • Zunächst werden alle benutzerdefinierten Ableitungsattribute aus Makros 1.1 einzeln erweitert. Das heißt, wenn Sie #[derive(Clone, Foo)] haben, leiten wir zuerst Foo wobei die Struktur eine #[derive(Clone)] Annotation hatte. Wenn diese #[derive] bestehen bleiben würden, würden wir das benutzerdefinierte integrierte Merkmal Clone ableiten.
  • Zweitens werden alle vom Compiler unbekannten Ableitungsattribute auf #[derive_Bar] Attribute erweitert. Dies ist nur ein Abwärtskompatibilitäts-Hack, den wir irgendwann entfernen sollten, und so werden Attribute verwendet, um erweitert zu werden.
  • Schließlich erweitert der Compiler alle bekannten und integrierten #[derive] -Attribute

Dies hat den überraschenden Effekt, dass Sie nicht von links nach rechts expandieren, aber denken Sie auch daran, dass dies nur #[derive] . Dies gibt dem Compiler maximales Wissen über die Strukturdefinition und wenn er eingebaute Merkmale erweitert, wissen wir, dass sich die Struktur des Typs niemals ändern wird.

Wie klingt das? Ich glaube, es löst alle Einschränkungen hier?


@nikomatsakis Ich bin mir nicht sicher, ob die Strategie, das Attribut auf impl , funktioniert, da andere benutzerdefinierte Ableitungsmodi theoretisch das Layout der Struktur ändern könnten, sogar die Arten der Felder. Dies würde die Annahmen des Compilers verletzen, als er zum ersten Mal erweitert wurde, denke ich.

Wurde die Reihenfolge, in der Derivate verarbeitet werden, jemals offiziell als von links nach rechts deklariert, über die Rust-Referenz oder irgendetwas? Im Allgemeinen spielt die Reihenfolge der Attribute jemals eine Rolle? Es hört sich so an, als wäre es nur ein Zufall, dass es auf diese Weise implementiert wurde, und Makroautoren hätten sich nicht auf eine Reihenfolge von links nach rechts verlassen sollen. Alex 'Vorschlag, benutzerdefinierte Elemente zu verarbeiten, wird zuerst abgeleitet, sodass sie niemals magische Attribute sehen, die vom Compiler hinzugefügt wurden. Dies ist sehr sinnvoll.

Ich möchte nur hinzufügen, dass mir die Idee nicht gefällt, dass benutzerdefinierte Ableitungen das Layout der Struktur ändern können. Ich möchte dies für etwas verwenden können, das sicherheitsrelevant ist. Betrachten Sie als Beispiel die Implementierung von rust-gc #[derive(Trace)] , die von rust-gc .

#[derive(Trace)]
struct Foo {
    a: Gc<i32>,
}

Erweiterung auf:

struct Foo {
    a: Gc<i32>,
}

unsafe impl Trace { // NOTE: Strawman impl
    unsafe fn trace(&self) { Trace::trace(&self.a) }
}

Wenn wir jedoch das Ändern der Felder in der Struktur zulassen, können wir eine benutzerdefinierte Ableitung von Evil definieren:

#[derive(Evil)]
struct Foo {
    a: Gc<i32>,
}

Erweiterung auf:

struct Foo {
    a: Gc<i32>,
    b: Gc<i32>,
}

Was, wenn wir sie kombinieren:

#[derive(Trace, Evil)]
struct Foo {
    a: Gc<i32>,
}

Erweiterung auf:

struct Foo {
    a: Gc<i32>,
    b: Gc<i32>,
}

unsafe impl Trace {
    unsafe fn trace(&self) { Trace::trace(&self.a) }
}

Welches ist eine unsolide Implementierung von Trace . Bei Verwendung mit rust-gc kann b eine baumelnde Referenz sein, die schrecklich unsicher und nicht gesund ist. Dies bedeutet, dass Trace für #[derive] bei einem Typ, der sehr unglücklich ist, keine sichere Sache mehr ist.

Ich persönlich bin der Meinung, dass ein gut erzogenes #[derive] das Layout / den Aufbau einer Struktur nicht verändert, und wenn dies der Fall ist, haben Sie einfach kein Glück. Die Fähigkeit einer benutzerdefinierten Ableitung, Attribute zu löschen, ist von entscheidender Bedeutung, und das Aufgeben ist kein Anfänger. Darüber hinaus weichen andere Implementierungen, die irgendeine Form von Whitelisting beinhalten oder so weiter, ziemlich stark von der einfachen Oberfläche ab, die wir heute haben.

Anders ausgedrückt, ich glaube nicht, dass die "Reinheit", dass #[derive] die Struktur niemals modifiziert, die Kosten wert ist.

Ich frage mich nur, ob es eine Möglichkeit gibt, Attribute zu löschen, ohne dass Felder hinzugefügt oder entfernt werden können (z. B. um zu überprüfen, ob die Felder in der neu analysierten Struktur mit der ursprünglichen Struktur identisch sind, und um Fehler zu beheben, wenn sie nicht vorhanden sind. t, aber nicht beschweren, wenn andere Dinge geändert werden).

Ich habe ein schlechtes Gefühl, wenn ich ableiten darf, um die Struktur zu ändern. Das Beispiel von @mystor ist das, woran ich

Ich denke, die Leute werden dies ausnutzen, wenn es verfügbar ist. Dies wird die Verbraucher dazu zwingen, über Details von benutzerdefinierten Ableitungsimplementierungen und deren Ausführungsreihenfolge nachzudenken.

Ich würde es vorziehen, wenn ich sagen könnte: "Hey, ich weiß nicht, was diese Ableitung bewirkt, aber ich verstehe die andere" ohne gegenseitige Abhängigkeit. Sonst wird es ein Schmerz im Zeh sein und ich glaube, dass es passieren wird.

Unterscheidet sich ein prozedurales Makro, das etwas Bösartiges tut, wirklich von einer Kiste, in der Sie etwas Bösartiges tun? Jede Kiste kann unsicheren Code enthalten, der etwas tut, was sie nicht tun sollte. Scheint so, als ob dieser Fall nur damit zusammenhängt, wie Sie die Vertrauenswürdigkeit von Code bestimmen, den Sie nicht selbst geschrieben haben, z. B. den Ruf der Community, die Quelle selbst zu überprüfen usw.

Ich glaube nicht, dass Kisten versuchen werden, etwas Bösartiges zu tun, sondern ich erwarte, dass sie "clever" sind und nette Tricks machen, um ihre Implementierung effizienter zu machen oder weil sie es können, und damit andere benutzerdefinierte Ableitungen beschädigt werden. Es würde mich nicht überraschen, wenn einige benutzerdefinierte Ableitungen Strukturen zu Strukturen hinzufügen, die nur in ihren Implementierungen verwendet werden, weil sie dies können, und diese dann so etwas wie Trace beschädigen.

@mystor Das klingt theoretisch relevant, aber denken Sie daran, dass Sie tatsächlich alle Felder einer Struktur in Rust bereitstellen müssen, sodass es viel weniger wahrscheinlich ist, dass dies stillschweigend funktioniert, als beispielsweise in C ++.

@alexcrichton

Ein Vorschlag, den ich haben könnte, ist, die Erweiterungsreihenfolge von #[derive] zu optimieren

Ich mag diese Idee. Vielleicht ist in Bezug auf die Dokumentation einfach zu sagen, dass die Reihenfolge der Erweiterung "undefiniert" ist. Wir haben PartialEq / Eq in den letzten Tagen erweitert, aber es gibt keinen strengen Grund dafür.

Was Ableitungen betrifft, die die Strukturdefinition ändern, stimme ich zu, dass dies etwas subtil klingt, mich aber nicht allzu sehr stört. Ich würde auch denken, dass das "automatische Einfügen" von Feldern sehr praktisch sein könnte - aber ich würde es vorziehen, wenn solche Modifikatoren unterschiedliche Attribute sind, anstatt in die Ableitungsliste aufgenommen zu werden, vor allem, weil wir nicht möchten, dass sich die Leute auf die Reihenfolge verlassen der Expansion gerade jetzt.

PS. Ich würde ernsthaft in Betracht ziehen, ein (deterministisches) RNG zu verwenden, das durch den Kisten-Hash oder ähnliches ausgesät wird, um die Erweiterung der benutzerdefinierten Ableitungen des Benutzers neu zu ordnen, sodass sich die Benutzer nicht auf die Erweiterungsreihenfolge verlassen können. Ich wollte dies schon immer in einem bestimmten Kontext tun, um implizite Abhängigkeiten zu vermeiden, bekam aber nie die Chance. ;)

EDIT: Ich habe es mir anders überlegt und sehe keinen Grund mehr, die Mutation der Struktur nicht mehr zuzulassen, aber hier ist mein ursprünglicher Kommentar zum Kontext

Nach meinem Verständnis sind dies die Argumente dafür, dass benutzerdefinierte #[derive] die Struktur mutieren können:

  • Es könnte in einigen Fällen nützlich sein (habe keine Beispiele gesehen, aber ich glaube, dass sie existieren)
  • Wir möchten in der Lage sein, Attribute zu entfernen, sobald sie verwendet wurden
  • Benutzerdefinierte abgeleitete Autoren erhalten mehr Macht

Die Argumente für das Hinzufügen von Einschränkungen zu benutzerdefinierten #[derive] -Implementierungen (z. B. das Erfordernis, dass der Name der Struktur und die Felder / Namen der Felder in der Struktur gleich bleiben) sind:

  • Es ermöglicht, dass der von einem benutzerdefinierten #[derive] generierte Code von der Struktur des Typs abhängt, von dem er für die Solidität abgeleitet wird (z. B. #[derive(Trace)] von rust-gc, die den tatsächlichen Hintergrundtyp sehen müssen). oder es ist nicht in Ordnung, möglicherweise auf subtile Weise nach dem freien Gebrauch).
  • Es verringert die Wahrscheinlichkeit impliziter Abhängigkeiten bei Makroerweiterungen, da weniger Informationen zwischen ihnen durch die Struktur übertragen werden

Meiner Meinung nach ist die Fähigkeit, solide benutzerdefinierte Ableitungsimplementierungen zu schreiben, die unsafe trait Impls oder Code generieren, der von unsicherem Code abhängt, äußerst wichtig, und ich glaube, dass es Möglichkeiten gibt, die meisten Fähigkeiten im ersten Abschnitt zu erreichen ( mit Ausnahme der Möglichkeit, Strukturfelder auf sichere Weise hinzuzufügen. Wenn wir keine Einschränkungen haben, denke ich nicht, dass Kisten wie rust-gc auf sichere Weise implementiert werden können. Ich habe zwei Ideen:

Idee 1

Lesen Sie vor dem Ausführen eines benutzerdefinierten Ableitungsdurchlaufs den Namen der Struktur und die Namen der einzelnen Felder ein. Wenn der Durchlauf abgeschlossen ist und die Struktur erneut analysiert wurde, überprüfen Sie, ob der Name der Struktur identisch ist und ob die Namen der einzelnen Felder (und die Anzahl der Felder) identisch sind. Wenn dies nicht der Fall ist, wird ein Fehler ausgelöst und der Build beendet.

Dies würde sicherstellen, dass die grundlegenden strukturellen Eigenschaften, von denen wir erwarten, dass benutzerdefinierte Ableitungs-Plugins abhängen, nicht beschädigt werden, und bedeutet, dass wir mehr solide, benutzerdefinierte Ableitungs-Plugins haben. Dies hat auch den Vorteil, dass es abwärtskompatibel mit dem aktuellen Ansatz ist. Wenn wir also entscheiden, dass es uns in Zukunft besser gefällt, können wir einfach umschalten und den Code von niemandem brechen. Es behandelt auch den nicht verwendeten Attributfall, genau wie heute.

Idee 2

Geben Sie jedem benutzerdefinierten Ableitungs-Plugin den gleichen TokenStream-Eingang (den im Programm geschriebenen Originaltext). Wenn das Ergebnis erneut analysiert wird, notieren Sie, welche Attribute in der Ausgabestruktur noch vorhanden sind. Wenn in jedem Ausgabe-Tokenstream ein Attribut vorhanden ist, beschweren Sie sich über ein nicht verwendetes Attribut.

Dies bedeutet, dass es unmöglich ist, Ordnungsabhängigkeiten zu haben (da konzeptionell jedes benutzerdefinierte Ableitungs-Plugin mit demselben Originalobjekt funktioniert), und es macht es auch unmöglich, die Struktur des Plugins zu vermasseln. Ich mag diese Idee, da sie sicherstellt, dass jede benutzerdefinierte Ableitung größtenteils vernünftig funktioniert und nur neue Elemente basierend auf der vorhandenen Struktur generiert. Dies wäre wahrscheinlich auch leicht in eine Lösung umzuwandeln, in die wir die aktuelle Lösung umwandeln könnten.

TL; DR

Zusammenfassend möchte ich verstehen, was der besondere Vorteil darin besteht, mutierende Strukturen zuzulassen und warum es die Sicherheitsbedenken überwiegt, sichere #[derive(Trace)] und immer korrekte #[derive(Serialize)] usw. möglich zu machen. Ich bin mir sicher, dass es einen guten Grund gibt, wenn wir den Weg der mutierenden Strukturen beschreiten, aber ich werde sehr traurig sein, den Namen meines benutzerdefinierten Trace-Derivats in #[derive(unsafe_Trace)] zu ändern.

Ich finde die Lösung von @alexcrichton ein guter Kompromiss. Ich würde definitiv erwarten, dass alle Änderungen, die einige benutzerdefinierte Ableitungen vornehmen, auf die Standardänderungen angewendet werden.

Obwohl @mystor einen guten Punkt hat, der zu unangenehmen Überraschungen führen kann, scheint die Möglichkeit, die struct zu ändern, obligatorisch. Auf der anderen Seite scheinen Kisten, die Anwendungsfälle von prozeduralen Makros, unsicheren Code- und Sicherheitsbedenken kombinieren, eher ungewöhnlich zu sein.

Off Topic: Bietet diese Implementierung dem Makro die Möglichkeit, Fehler ordnungsgemäß zu melden?

Ich mag die Idee von @nikomatsakis , die Erweiterungsreihenfolge von benutzerdefinierten bestimmen . Das würde definitiv dazu beitragen, zu verhindern, dass "schlechte" Gewohnheiten zu populär werden - und sollte nicht zu aufwändig sein.

@mystor Eine dritte Option wäre, diese Sicherheitsüberprüfungen nur einmal Ableitungen angewendet wurden. Das wäre kein 100% iger Klang (zwei Ableitungen könnten dasselbe Feld hinzufügen und entfernen), aber im Hinblick auf eine heuristische Gegenmaßnahme sollte es ausreichen, zu verhindern, dass überhaupt versucht wird, die Strukturdefinition in einer benutzerdefinierten Ableitung zu ändern.

Ich sehe die Besorgnis um Strukturänderungen wirklich nicht. Ein Feld kann nicht unsichtbar hinzugefügt werden, während der Initialisierung muss etwas beachtet werden. Wenn Sie sich wirklich Sorgen darüber machen, können Sie Ihre Ableitung schreiben, um Code zu generieren, der nicht kompiliert werden kann, wenn die gesamte Struktur nicht so einfach angezeigt wird.

@sgrif wahrscheinlich in _most_ Fällen wahr - aber nicht so sehr, wenn Sie auch das Standardmerkmal oder etwas Äquivalentes ableiten und verwenden.

@sgrif PS: Es ist wahr, dass die meisten

Der allgemeine Anwendungsfall für das Anwenden von Makros auf Strukturen ist sicherlich eine Kombination von Strukturänderungen + Dekorationen. Aber ich erwarte ein allgemeines Verhältnis von "vielen Dekorationen" mit "nur wenigen Änderungen". Es ist gut, hier eine klare Trennung zu haben, da dies die Lesbarkeit und Flexibilität verbessert.

  • Flexibilität: Angenommen, Sie möchten zwei benutzerdefinierte Ableitungen anwenden, die beide ausführen, dh ändern und dekorieren. Unabhängig davon, wie Sie sie bestellen, erhalten Sie möglicherweise nicht das gewünschte Ergebnis. Wenn die Änderung jedoch über einen anderen Mechanismus erfolgt und alle Dekorationen anschließend angewendet werden, haben Sie die Flexibilität, mehrere Änderungen und Dekorationen auf kontrollierbarere Weise zu kombinieren.
  • Lesbarkeit: Wenn Sie einen anderen Code lesen und 10 Ableitungen auf eine Struktur angewendet werden und eine davon die Struktur ändert, benötigen Sie mehr Zeit, um dies herauszufinden.

Also, während ich diesen Teil von @mystors Argumentation überzeugend finde:

Es ermöglicht, dass der von einem benutzerdefinierten # [Ableiten] generierte Code von der Struktur des Typs abhängt, von dem er für die Solidität abgeleitet wird (z. B. # [Ableiten (Trace)] von rust-gc, das den tatsächlichen Hintergrundtyp sehen muss, oder es ist nicht gesund, möglicherweise auf subtile Weise nach dem freien Gebrauch).

Ich denke, dass der Versuch, dies durchzusetzen, der falsche Weg sein kann, um Dinge zu tun. Insbesondere wollen wir wahrscheinlich die Möglichkeit , Strukturdefinitionen im allgemeinen Fall zu ändern (ja, es wird nicht transparent sein, aber so was). Was bedeutet, dass die Idee, einen "Sound" Trace , der sicher sein kann, dass sich die Struktur danach nicht ändert, möglicherweise auf eine andere Weise gelöst werden muss. Überlegen Sie, ob Dekorateure von unten nach oben angewendet werden:

#[mangle] // <-- custom decorator that does bad things to struct definition
#[derive(Trace)]
struct Foo {
    x: T, y: U
}

Ein Gedanke könnte sein, dass das Trace impl so geschrieben werden kann, dass _it_ nicht kompiliert werden kann, wenn sich die Definition von struct ändert. Zum Beispiel:

unsafe impl Trace for Foo {
    fn trace(&self) {
        let &Foo { ref x, ref y } = self;
        <T as Trace>::trace(x);
        <U as Trace>::trace(y);
    }
}

Beachten Sie jedoch, dass #[mangle] auch mit Ihrem Gerät schrauben kann, wenn es wirklich teuflisch ist. =) Hier können wir nur so viel tun.

Als Beobachter dieser Gespräche würde ich mich über die formelle oder informelle Regel freuen, dass #[derive] nur impl Blöcke hinzufügen und eine Geschwisteranmerkung einführen darf ( #[mangle(Foo, Bar)] klingt gut zu mir 😸) das ist dafür gedacht, die Struktur eines Typs zu ändern. #[mangle] könnte eine definierte Bewertungsreihenfolge haben.

Es muss wahrscheinlich eine definierte Auswertungsreihenfolge zwischen Anmerkungen geben, falls dies noch nicht geschehen ist.

Ich denke, meine Meinung dazu hat sich entspannt. @nikomatsakis macht einen guten Punkt, dass wir, selbst wenn wir mutierende Strukturen in # [ableiten] loswerden würden, nicht davonkommen würden, Annahmen über Strukturlayouts im Code zu treffen. Der Trick let Foo{ ... } scheint zu funktionieren, um sicherzustellen, dass das Layout in vernünftigen Fällen korrekt ist. Wir müssen es jedoch wahrscheinlich irgendwo dokumentieren, damit nicht jeder es unabhängig entdecken muss.

@ Nikomatsakis

  • mh .. es könnte die zusätzliche Regel geben, dass benutzerdefinierte Dekoratoren vor dem Ableiten angewendet werden müssen, und _derives darf das Element nicht ändern. Dies würde es immer noch ermöglichen, die Ableitungsmechanik allgemein zu härten / zu reinigen.

Ich bin aber auch froh zu sehen, dass es eine andere Möglichkeit gibt, benutzerdefinierte Ableitungen individuell gegen spätere Änderungen zu härten. :-)

In Bezug auf Fälle, in denen das Innere des Elements geändert werden muss, auf das das Attribut angewendet wird, bin ich gerade auf den Thread "Feedback vor der Implementierung für Qt mit Rust" auf u.r-l.o gestoßen und habe diesen Beitrag verfasst:

https://users.rust-lang.org/t/pre-implementation-feedback-for-qt-with-rust/7300/19

Eine bemerkenswerte Facette ist, dass ich hier vorschlage, ein #[derive] (oder ähnliches) auf ein _trait_ anstatt auf eine Struktur anzuwenden - und das darin hinzugefügte Element wäre eine const Trait-Methode .

Ähnlich wie bei den oben angesprochenen Frachtproblemen bin ich mir nicht sicher

#[macro_use]
extern crate double;

Importieren von prozeduralen Makros aus einer Kiste namens double . Da wir zur Kompilierungszeit Laufzeitcode (wie in echtem Ruest) verwenden, sollte es eine phaseninkrementierende Importanweisung geben, die Rackets (require (for-syntax ...)) . [Das Racket-Dokument lautet https://docs.racket-lang.org/reference/require.html#% 28form ._% 28% 28lib._racket% 2Fprivate% 2Fbase..rkt% 29._for-meta% 29% 29, Leider kann ich nicht herausfinden, wie ich den richtigen Abschnitt verlinken kann.]

Der Blog-Beitrag http://blog.ezyang.com/2016/07/what-template-haskell-gets-wrong-and-racket-gets-right/ weist auf die in Template Haskell gemachten Phasenfehler hin und kann von Interesse sein. - Ich möchte nicht die gleichen Fehler in Rust machen.

@ Ericson2314 Der Unterschied in Rust besteht darin, dass double bereits für eine bestimmte Phase kompiliert wurde.
Das heißt, die Kiste exportiert nur die Makro- / Modifikator-Schnittstelle, als ob sie mit definiert worden wäre, z. B. macro_rules .
Es wäre interessant, Kisten erstellen zu können, die sowohl prozedurale Makros als auch die zugrunde liegenden Rust-Elemente (die das prozedurale Makro bilden) exportieren, aber bisher scheint dies in keiner Weise vorgeschlagen zu werden.

Es mag sinnvoll sein, der Kiste zu erlauben, viel darüber zu entscheiden, was und wie exportiert werden soll, aber es scheint kontraproduktiv, nur einen Systemgroßhandel von einem LISP mit einem anderen Kompilierungsmodell zu übernehmen.

Ja @eddyb Ich bin skeptisch gegenüber dieser Methode "

Nominiert für eine lange Teamdiskussion zu: einem Stabilisierungsplan.

Auf der Serde-Seite finden Sie hier eine kurze Liste der verbleibenden Probleme, bevor wir die Unterstützung des vorhandenen Compiler-Plugins einstellen und Macros 1.1 offiziell für alle nächtlichen Benutzer empfehlen können: https://github.com/serde-rs/serde/issues/545. Das einzige, was wir von Rust brauchen, ist, dass # 36211 repariert wird. Alles andere machen wir schnell Fortschritte.

Ich habe eine offene PR, die unser rustc_macro ohne Verwendung von syntex implementiert, sodass wir uns keine Gedanken mehr über die Kompilierungszeit machen müssen. Https://github.com/serde-rs/serde/pull/548.

EDIT: Egal, # 36211 hat nur die alte Syntex-Implementierung beeinflusst

Ich werde versuchen, den Dieselhafen bis Freitag fertig zu stellen, damit ich bestätigen kann, dass dies alles tut, was wir zu diesem Zweck brauchen.

@dtolnay gegeben serde-rs / serde # 548, gibt es noch Blocker für serde?

@sgrif super , danke! Wenn Sie dies getan haben (oder zuvor), können Sie hier einen Kommentar zu den verbleibenden Blockern abgeben, auf die Sie gestoßen sind?

Ja, werde ich.

Am Dienstag, 27. September 2016, 19:29 Uhr Alex Crichton [email protected]
schrieb:

@dtolnay https://github.com/dtolnay gegeben serde-rs / serde # 548
https://github.com/serde-rs/serde/pull/548 , sind noch welche übrig
Blocker für Serde?

@sgrif https://github.com/sgrif super , danke! Sobald du das getan hast
(oder vorher) könnten Sie hier mit den verbleibenden Blockern, die Sie haben, einen Kommentar abgeben
angetroffen?

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/rust-lang/rust/issues/35900#issuecomment -250028743,
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/ABdWK7MnA1rpn-WGji8gwAT2JCIqB4LFks5quaa-gaJpZM4JqEAX
.

@alexcrichton

Gibt es bei serde-rs / serde # 548 noch Blocker für serde?

Nein, wenn @ oli-obk oder @erickt diese PR heute oder morgen überprüfen, kann ich am nächsten Tag alles herausbringen und ein paar prominente Benutzer wie Rusoto migrieren.

@dtolnay Ich benutze serde_macros in Ruma sehr gerne und möchte Ihnen auch helfen, serde_derive zu testen, wenn Sie mehr Augen benötigen.

Tatsächlich verwende ich auch diesel_codegen, daher ist Ruma ein gutes Testfeld für die Macros 1.1-Version dieser beiden Bibliotheken.

Ich habe Serde 0.8.10 veröffentlicht

Fügen wir ein Kontrollkästchen für "Frachtdokument funktioniert" hinzu - https://github.com/rust-lang/cargo/issues/3132.

@dtolnay fertig!

Ich bin mir nicht sicher, ob es sich um serde_derive-Fehler oder um rustc / rustdoc-Fehler handelt, aber ich stelle zwei Probleme in generierten Dokumenten fest:

  • Am Anfang der generierten Dokumentzeichenfolgen von Typen, die benutzerdefinierte Ableitungen verwenden, wird ein Literal "///" angezeigt.
  • Deserialize und Serialize werden im Abschnitt "Trait-Implementierungen" für Typen, die benutzerdefinierte Ableitungen verwenden, nicht angezeigt.

Am Anfang der generierten Dokumentzeichenfolgen von Typen, die benutzerdefinierte Ableitungen verwenden, wird ein Literal "///" angezeigt.

Dies war ein syn Fehler. Ich habe 0.8.2 mit einem Fix veröffentlicht, also cargo update , um es aufzunehmen.

Ich weiß nichts über den zweiten, überlasse das @alexcrichton.

@ Jimmycuadra

  • Deserialize und Serialize werden im Abschnitt "Trait-Implementierungen" für Typen, die benutzerdefinierte Ableitungen verwenden, nicht angezeigt.

Das klingt ziemlich faul! Sieht so aus, als wäre dies ein Fehler in Rustdoc, aber vielleicht kein neuer. Zum Beispiel zeigt rustdoc dies bei der Implementierung von Copy :

#[derive(Clone)]           
pub struct Point {         
    x: i32,                
    y: i32,                
}                          

const _FOO: () = {         
    impl Copy for Point {} 
    ()                     
};                         

Dieses Muster wird von serde-derivate verwendet, um Impls zu generieren. @dtolnay hatte serde_macros dieselbe Generationsform? Wäre es einfach, sich vorerst zu entfernen, um das Dokumentationsproblem zu lösen?

@jimmycuadra möchten einen separaten Fehler öffnen, um das Rustdoc-Problem zu verfolgen?

@dtolnay hatte serde_macros auch die gleiche Form der Generierung?

Ja.

Wäre es einfach, sich vorerst zu entfernen, um das Dokumentationsproblem zu lösen?

Soweit ich weiß, ist dies die einzige Lösung, die diese beiden Einschränkungen erfüllt:

  • Muss serde nicht in der Kistenwurzel unterstützen, dh nicht ::serde . Dies ist häufig der Fall, wenn Benutzer in einem separaten Modul serde impls hinter ein Feature-Flag setzen.
  • Muss in der Lage sein, private Typen aus dem umgebenden Modul zu verwenden.

Weitere Informationen finden Sie unter https://github.com/serde-rs/serde/issues/159 .

@dtolnay ah ok, um das zu verdeutlichen, @jimmycuadra das ist keine Regression von früher, oder?

Update: Mit dem Port noch nicht ganz fertig, aber fast da. Keine anderen Probleme als Dinge, die ich bereits gemeldet habe, oder kleinere Einschränkungen, von denen wir wussten, dass sie uns begegnen würden. Wahrscheinlich ist es sicher, das Kontrollkästchen "Diesel Works" zu aktivieren. Wir werden morgen eine Veröffentlichung für Macros 1.1 veröffentlichen, falls dies heute Abend nicht geschieht.

Für diejenigen, die mitmachen, habe ich # 36945 geöffnet, um rustc_macro in proc_macro praktisch überall umzubenennen, was der Konsens über den Namen dieser Kiste zu sein scheint.

Es sieht so aus, als ob die fehlenden Merkmalimplementierungen in rustdoc (die jetzt in einem anderen Problem verfolgt werden) nicht spezifisch für Makros 1.1 sind. Sicher zu ignorieren.

Ich habe versucht, meine mockers -Bibliothek vom Compiler-Plugin auf Makros 1.1 zu portieren, und habe den Fehler "Fehler: Benutzerdefinierte Ableitungsattribute können nur auf Struktur- / Aufzählungselemente angewendet werden" erhalten. Mit dem Compiler-Plugin ist es möglich (wenn auch etwas seltsam), "Ableiten" für Merkmale zu verwenden. Habe ich hier kein Glück?

@kriomant interessant! Ich denke, dass dies heute tatsächlich ein Fehler bei der benutzerdefinierten Ableitung sein kann, da ich nicht sicher bin, ob es jemals beabsichtigt war, #[derive] auf ein Merkmal anzuwenden ...

Ich denke, um konservativ zu sein, werden wir wahrscheinlich noch nicht die Ableitung von Merkmalen stabilisieren, aber wir könnten vielleicht eine instabile Funktion hinzufügen. Gedanken @ rust-lang / lang?

@alexcrichton Was ist ein Unterschied zwischen Merkmalen und Strukturen in Bezug auf custom_derive?

@alexcrichton Können wir Merkmale unterstützen?

Wie oben erwähnt , ist es wahrscheinlich ein Fehler, dass benutzerdefinierte Ableitungen für Merkmale jemals zulässig waren, und dies wurde im RFC nicht spezifiziert, sodass vor der Erweiterung weitere Diskussionen erforderlich wären. In jedem Fall ist es unwahrscheinlich, dass die Unterstützung für das Ableiten von Merkmalen im ersten Durchgang stabilisiert wird, aber wir könnten ein separates Feature-Gate in Betracht ziehen.

Ich persönlich möchte kein Derive-on-Trait hinzufügen, da dies viel mehr im Sinne eines benutzerdefinierten Attributgebiets zu sein scheint, als sich selbst abzuleiten. (zB widerspricht dem ursprünglich erstellten Geist von #[derive] )

Ich würde es vorziehen, wenn die benutzerdefinierte Ableitung genau den gleichen Regeln folgt wie die reguläre Ableitung. Wir möchten das vielleicht später ändern, aber ich denke, es sollte RFC-fähig sein (ich bin auch ziemlich kalt bei der Idee, tbh, aber ich könnte meine Meinung durch einen überzeugenden Anwendungsfall und eine anständige Behandlung verschiedener Randfälle ändern ).

@alexcrichton

Ich denke, um konservativ zu sein, werden wir die Ableitung von Merkmalen wahrscheinlich noch nicht stabilisieren, aber wir könnten vielleicht eine instabile Funktion hinzufügen. Gedanken @ rust-lang / lang?

👍 von mir.

Ok, instabile Funktion ist gut, aber es bedeutet, dass meine Bibliothek immer noch nicht stabil funktioniert (ohne Codegenerierung). Und die Syntax macro!(…) wird auch nicht von "Makros 1.1" abgedeckt. Stimmt das?

Ich besuche das Proc-Makro RFC erneut und es war geplant, dass sich Makrokisten als #[cfg(macro)] deklarieren sollten. Wir benötigen dies nicht für Makros 1.1, aber wir benötigen einen speziellen Kistentyp. Die beiden Mechanismen sind etwas orthogonal - der erste beschreibt die Kompilierungsphase, der zweite die Art der Kiste. Sie überschneiden sich aber auch etwas - letzteres impliziert Ersteres. Ersteres skaliert auch auf die Deklaration von Proc-Makros und Nicht-Makro-Funktionen in denselben Kisten, während letzteres dies nicht tut.

Ich bin mir nicht sicher, ob wir hier etwas ändern müssen, wir könnten wahrscheinlich den Vorschlag für Proc-Makros in Abwärtskompatibilität hacken (es entfällt absichtlich die Beschreibung des Mechanismus zum Laden von Makros und damit der Kistentypen). Aber etwas zum Nachdenken während der Stabilisierungsphase.

@kriomant richtig, ja

@nrc im Moment ist das eigentlich definiert als cfg(rustc_macro) (obwohl bald auf proc_macro geändert wird). Wir brauchen es nicht, nein, aber ich dachte, es wäre notwendig für das Konzept, sowohl eine Kompilierungszeit als auch zur Laufzeit mit einer Kiste zu verknüpfen? Das heißt, wir würden die Proc-Makro-Kiste zweimal kompilieren: einmal mit dem Kistentyp proc-macro und einmal mit dem Kistentyp rlib , und letzterer würde keine Verknüpfung mit libsyntax oder Ähnlichem herstellen Das.

Im Moment scheint es in Ordnung zu sein, es nicht anzufordern, obwohl ich denke, dass dies zu einem späteren Zeitpunkt bedeuten würde, dass Sie sich für die Laufzeitunterstützung anmelden müssen? (zweimal die Kiste zusammenstellen)

@alexcrichton Könnte #[proc_macro_derive] bedeuten? Genauso wie #[test] #[cfg(test)] impliziert.
(Das bedeutet nicht, dass wir es jetzt hinzufügen müssen, nur dass der schlimmste Fall, wenn wir cfg hinzufügen, nicht verwendete Elementwarnungen sind.)

@eddyb Was ist mit extern crate proc_macro; ? Ich habe das Gefühl, dass diese auch brechen würden.

@mystor Es ist nur eine normale Kiste mit einer Reihe von Typen und Geräten.

Verknüpft es nicht auch mit libsyntax , was bedeutet, dass eine Kiste, die eine proc_macro-Kiste verwendet und kompilieren wollte, auch die Kompilierungssyntax kreuzen müsste?

@eddyb yes #[proc_macro_derive] kann automatisch ignoriert werden, aber das Problem ist, dass wir auch alles ignorieren müssen, was diese Funktionen transitiv erreichen (wie das extern crate proc_macro ). Obwohl es "nur eine Kiste" ist, hat es schwerwiegende Auswirkungen auf die Laufzeit (dynamische Verknüpfung, nicht verfügbar für überkompilierte Ziele usw.).

Beachten Sie, dass Tests #[cfg(test)] , daher erscheint es mir vernünftig, dass wir immer noch #[cfg(proc_macro)] für den gleichen Zweck geben.

@alexcrichton _Ideally_, die Kiste hätte nichts mehr als das, was exportiert wird und würde keine dynamische Verknüpfung oder ähnliches erfordern, nur libstd . Ich konnte jedoch feststellen, dass es in #![no_std] Fällen zu Problemen kam.

Klingt so, als müssten wir von Anfang an die Doppelkompilierung durchführen, wenn wir alles fangen wollen: dis ernannt:.

EDIT : Warte, was denke ich überhaupt? Es erfordert einen benutzerdefinierten Kistentyp. Die Doppelkompilierung gilt für reguläre Kisten, die auch prozedurale Makros / Attribute / Ableitungen / etc. Exportieren. Also vorerst nicht relevant.
Aber wir könnten zumindest #[cfg(proc_macro)] einführen, das immer für den neuen Kistentyp festgelegt ist.

🔔 Diese Funktion tritt in den letzten Kommentarzeitraum ein , um sich am Ende dieses Veröffentlichungszyklus zu

Die Gründe für eine Stabilisierung sind jetzt:

  • Die API-Oberfläche dieser Funktion ist von Natur aus äußerst konservativ. Gleichzeitig ist es vorwärtskompatibel mit dem De-facto-Plan für prozedurale Makros
  • Die Funktion wird durch Serde und bald auch Diesel schnell verbreitet - obwohl die weit verbreitete Verwendung vor allem als Kunde von benutzerdefinierten Ableitungen erfolgt.
  • Wenn wir jetzt in FCP einsteigen, haben wir drei Monate Zeit, bevor die Funktion tatsächlich im Stall ausgeliefert wird. Dies sollte ausreichend Zeit sein, um verbleibende Probleme zu beheben.

Beachten Sie, dass der Name auf proc_macro verschoben wird, basierend auf einem früheren Konsens in diesem Thread. Wir können diesen und alle anderen Feinheiten für den Rest dieses Release-Zyklus weiter radeln.

Das zweimalige Zusammenstellen der Kiste klingt sehr grob: Das Scoping funktioniert nicht richtig. Wirklich so etwas wie extern! crate needed_for_my_inline_proc_macros; ist eine viel schönere Lösung.

@aturon was, haben die Leute nicht erst vor diesen Tagen damit

@ Ericson2314 Es ist etwas länger her, aber wie der FCP-Kommentar erklärt, beträgt die _minimale_ Zeit vor dem Versand an den stabilen Kanal drei Monate, was unserer Meinung nach für diese extrem enge Schnittstelle mehr als ausreichend ist.

Beachten Sie, dass der FCP selbst eine 6-wöchige Angelegenheit ist, was nicht unbedingt bedeutet, dass wir ihn sogar auf den Weg der Stabilisierung bringen würden. Indem wir den Prozess jetzt zumindest starten, bieten wir die Möglichkeit, diese Funktion innerhalb von 3 Monaten zu versenden, sofern bis dahin keine Probleme entdeckt wurden.

Ah ok. Ich erinnerte mich an den 3-Monats-Teil, vergaß aber den Ort des FCP in diesen 3 Mündern - damit es nicht 3 Monate ab dem 22. August waren. Vergiss das Timing dann.

Ich denke, die von mir angesprochenen Phasenprobleme wirken sich sogar auf diesen kleinen Teil der Geschichte der Proc-Makros aus, daher würde ich mir wünschen, dass diese angesprochen werden.

@alexcrichton die rustc_copy_clone_marker und andere rustc_attrs beißen uns immer noch. Siehe https://github.com/serde-rs/serde/issues/577.

#[derive(Copy, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct MyStruct {
    value: i64,
}

Die Problemumgehung besteht darin, die Bestellung zu tauschen, aber ich dachte, ich würde prüfen, ob dies reparabel ist.

#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone)]
pub struct MyStruct {
    value: i64,
}

Ich habe die Portierung von https://github.com/sfackler/rust-postgres-derive auf Makros 1.1 abgeschlossen. Es war relativ schmerzlos, aber der Umgang mit Attributen, die von mehreren Ableitungen gelesen werden, ist ein großer Schmerz. Serde ist auf dasselbe gestoßen, aber es erfordert eine Menge seltsamer Logik, um beide Ableitungen gleichzeitig zu erweitern: https://github.com/sfackler/rust-postgres-derive/blob/master/postgres-derive-internals/ src / lib.rs # L26

Es sollte die Stabilisierung nicht blockieren, aber ich denke, es ist ein relativ großes ergonomisches Problem auf der Autorenseite, das wir wahrscheinlich bald angehen sollten.

Ich habe quote 0.3.0 mit Unterstützung für ausgefallene Wiederholungen im macro_rules! -Stil veröffentlicht (danke @nrc für den

pub fn enum_body(name: &str, variants: &[Variant]) -> Tokens {
    let num_variants = variants.len();
    let variant_names = variants.iter().map(|v| &v.name);

    quote! {
        if type_.name() != #name {
            return false;
        }

        match *type_.kind() {
            ::postgres::types::Kind::Enum(ref variants) => {
                if variants.len() != #num_variants {
                    return false;
                }

                variants.iter().all(|v| {
                    match &**v {
                        #(                           // \
                            #variant_names => true,  //  |----- new feature
                        )*                           // /
                        _ => false,
                    }
                })
            }
            _ => false,
        }
    }
}

Ich bin neugierig, warum # und nicht $ ?

EDIT : Ich habe immer gedacht, dass die Anführungszeichen-Syntax ${...} aber vielleicht bin ich einfach zu sehr an ES6-Vorlagenliterale gebunden, selbst wenn es mehrere Jahre her ist. \{...} funktioniert auch, obwohl es anderswo nützlich wäre.
Keine Klammern scheinen schwer zu erkennen, aber ich sollte mir keine Sorgen machen.

Ich bin neugierig, warum # und nicht $ ?

Weil es sich um ein macro_rules-Makro handelt und ich nicht tun kann, was ich will: smile:. Ein $v wird als einzelnes Token abgeglichen und es gibt keine Möglichkeit, von $v zur Verwendung der Variablen v . Im Gegensatz dazu sind #v zwei Token, sodass ich sie separat abgleichen und Dinge mit der Identität tun kann.

macro_rules! demo {
    ($tt:tt) => {};
}

fn main() {
    demo!($v);
}

Warten Sie, bis alles in macro_rules ?! Die vergessenen Makros 1.1 wurden nur für einen Moment abgeleitet. Das heißt, $x als Token ist ein Designfehler IMO. cc @jseyfried

@dtolnay

@alexcrichton der rustc_copy_clone_marker und andere rustc_attrs beißen uns immer noch. Siehe serde-rs / serde # 577.

Oh je, das scheint schlecht! Ich werde die Liste oben aktualisieren. @nrc oder @jseyfried Gedanken darüber, wie wir das angehen könnten? Ich bin nicht allzu vertraut mit der Erweiterungsreihenfolge von allem, aber wenn ein #[derive] -Attribut erweitert wird, könnte es vielleicht versuchen, alle zukünftigen zu schlürfen und dies zuerst zu tun?

@sfackler

Der Umgang mit Attributen, die von mehreren Ableitungen gelesen werden, ist ein großer Schmerz

Ich bin mir nicht sicher, ob ich dem Beispiel, mit dem Sie verlinkt haben, ganz gefolgt bin. Könnten Sie das näher erläutern?

@alexcrichton Nehmen

#[derive(ToSql, FromSql)]
enum Foo {
    #[postgres(name = "bar")]
    Bar
}

Das Attribut #[postgres] wird verwendet, um anzupassen, wie die Implementierungen ToSql und FromSql generiert werden. Es muss vor der endgültigen Ausgabe entfernt werden, da es ansonsten ein unbekanntes Attribut ist, aber die Implementierungen ToSql und FromSql werden separat ausgeführt. Wenn Sie dies auf naive Weise tun, indem Sie nur die Implementierung generieren und dann die Attribute entfernen, fehlen beim zweiten Ableiten die Anpassungen.

serde-derive und postgres-derive hacken dies jetzt, indem die Implementierung für beide abgeleiteten Impls an dieselbe Funktion weitergeleitet wird, die beide gleichzeitig generiert. Wir müssen die #[derive] für diejenige wieder anbringen, die gerade aufgerufen wird, seit der Compiler sie entfernt hat, und sie dann zur Erweiterung weiterleiten :

@sfackler Ich denke, Sie können dies auch tun, indem Sie die zusätzlichen Attribute nur entfernen lassen, wenn keine Ableitung von demselben Implementierer mehr vorhanden ist. Ihr Weg könnte besser sein.

@sfackler ah ok, macht vollkommen Sinn. Vielen Dank!

Ich frage mich, ob diese Logik vielleicht spröde ist. Wenn Sie beispielsweise ToSql wie wird die zukünftige Erweiterung von FromSql erkannt? Es könnte vielleicht hinter einem #[cfg] wie @dtolnay stehen, das oben erwähnt wurde ? Es ist also möglicherweise nicht in allen Fällen möglich, dies zu erkennen?

@alexcrichton Ja, es ist spröde, weshalb eine echte Lösung wichtig erscheint.

Würden #[cfg] und #[cfg_attr] nicht verarbeitet, bevor sie abgeleitet werden?

Da die Makros 1.1-API für Zeichenfolgen funktioniert, kann ich mir nur drei Lösungen vorstellen:

  1. Lass es wie es ist und warte auf Makros 2.0
  2. Nicht verwendete Attribute in benutzerdefinierten Ableitungen zulassen
  3. Erweitern Sie die Makros 1.1-API, indem Sie einem benutzerdefinierten Derivat erlauben, Attributnamen auf eine bestimmte Whitelist für das aktuelle Element zu übertragen

Jede Option hat ihre eigenen Vor- und Nachteile.
con 1: spröde Workarounds für die Zwischenzeit
con 2: Einige nicht verwendete Attribute werden nicht erkannt
con 3: mehr API-Oberfläche für eine Zwischenlösung

Ich habe überlegt, Attribute in #[used(...)] zu verpacken, um sie zu behalten und gleichzeitig auf die Whitelist zu setzen, aber es ist ziemlich albern und zu instabil.

@alexcrichton

Wenn ein #[derive] -Attribut erweitert wird, könnte es versuchen, alle zukünftigen zu schlürfen

Ich mag diese Idee. Da #[cfg] s und und #[cfg_attr] s vor #[derive] s verarbeitet werden, sind #[cfg] -geschützte #[derive] kein Problem für diesen Ansatz (oder für @sfacklers analoge Lösung für das Problem des

Dieser Ansatz würde die Lösung von @sfackler etwas einfacher machen, da andere relevante dass der Vorschlag von @eddyb die Dinge vereinfachen würde).

Eine Möglichkeit für das Problem mit den Steuerungsattributen besteht darin, Rückrufe nach der Erweiterung hinzuzufügen, ähnlich wie bei syntex: https://github.com/sfackler/rust-postgres-derive/blob/master/postgres-derive-codegen/src/lib. rs # L23 -L50

Ihre abgeleiteten Impls können unabhängig sein und keine Strip-Control-Attribute, und Sie können einen Durchgang registrieren, der ausgeführt wird, nachdem alle Erweiterungen zur Bereinigung durchgeführt wurden.

@dtolnay Ihre bisherige Sorge über mehrere #[derive] Attribute werden sollte fixiert kurz dank @jseyfried

Ich habe damit mit den Paketen syn und quote und hatte eine wirklich positive Erfahrung. Dies ist eine großartige Funktion, wenn es sich stabilisiert.

Das Problem mit den Attributen beißt derzeit mein Gerät, da es der Ableitungslogik von serde nachgeschaltet ist.

Das Ignorieren nicht verwendeter Attribute in abgeleitetem Code oder das Zulassen, dass späte Erweiterungen, die nach regulären Ableitungen ausgeführt werden, diese entfernen, scheinen mir vernünftige Lösungen zu sein.

Es wäre ideal, wenn Makros nicht die Möglichkeit hätten, den ursprünglichen Token-Stream zu mutieren. Es scheint eine gefährliche Funktion zu sein, auf die man sich zwischen jetzt und Makros 2.0 in freier Wildbahn verlassen kann und die daher später nur schwer zu entfernen ist.

Ich hatte also ein fehlendes Semikolon in einem Typpfad innerhalb einer generischen Funktion und erhielt die folgende Fehlermeldung von rustc. Ich frage mich, ob weitere Informationen bereitgestellt werden könnten, z. B. welches Ableitungsmakro den Lex-Fehler verursacht hat, welches Token ihn verursacht hat usw.

error: custom derive attribute panicked
  --> src/simple.rs:69:1
   |
69 | struct C(u64);
   | ^^^^^^^^^^^^^^
   |
   = help: message: Failure parsing derived impl: LexError { _inner: () }

Ich habe einen Testfall für den LexError erstellt. Dieser scheint am 13. Oktober jeden Abend zu passieren (Rustup hatte keine Updates). Https://github.com/keeperofdakeys/proc_macro_derive_test.

@ rust-lang / lang Ich denke, wir müssen die Frage diskutieren, ob Ableitungen ihr kommentiertes Element ändern können. Obwohl es eine einfache Lösung ist, dies zuzulassen, scheint es den Erwartungen der Benutzer zu widersprechen und ziemlich unbeliebt zu sein. Ich bevorzuge immer noch die aktuelle Lösung - die Einfachheit ist schön, und keine der vorgeschlagenen Alternativen scheint mir besser zu sein.

Wir benötigen eine Lösung, um Attribute zu steuern, wenn das Element geändert werden kann. Ich glaube nicht, dass es mir besonders wichtig ist, die Fähigkeit zum Ableiten beizubehalten, um das Element zu ändern, solange (in Zukunft) nicht abgeleitete Attributmakros dies können.

Jemand sollte "ändern" definieren. Sprechen wir über das Ändern der Mitglieder der Struktur oder nur über das Entfernen von Attributen, um zu verhindern, dass sich der Compiler über unbekannte Attribute beschwert?

Wenn wir über das Ändern der Mitglieder der Struktur sprechen, besteht meiner Meinung nach die beste Lösung darin, nur ein strukturmodifizierendes Makro für jede Struktur zuzulassen, das vor allen anderen erweitert wird. Das hätte ein klar definiertes und (meiner Meinung nach) erwartetes Verhalten.

Wenn wir nur über das Entfernen von Attributen sprechen, sollte es wahrscheinlich eine Möglichkeit geben, Attribute in den Makromechanismus selbst zu integrieren, anstatt dies dem Ermessen des Makros zu überlassen.

Meine Intuition darüber, wie sich Ableiten verhält, ist, dass es Impls hinzufügt. Für den Endbenutzer sollten Ableitungen Impls hinzufügen und nichts anderes tun. Insbesondere sollten sie die Struktur nicht in einer Weise modifizieren, die für andere Ableitungen von Bedeutung ist, und daher sollten sie auftragsunabhängig sein. Sind wir uns einig, dass sich ein Derivat so verhalten sollte, oder denken die Leute, dass Ableitungen auch Transformationen für den angehängten Typ durchführen können sollten?

Wenn wir uns darin einig sind, stellt sich die Frage: Wollen wir, dass die von uns bereitgestellte Schnittstelle dies erzwingt, oder möchten wir es den Autoren überlassen, dies durchzusetzen? Hier scheint es zwei gegenläufige Probleme zu geben:

  • Die Durchsetzung des Verhaltensvertrags der Ableitung scheint mir sicherlich eher die Rust-Lösung zu sein.
  • Es ist eine Menge Herausforderungen, serde und deisel zu bringen, mit dieser Einschränkung zu arbeiten.

Und natürlich scheint es nicht damit zu tun zu haben, wie andere Attribute die Struktur modifizieren können.

Es ist erwähnenswert, dass das aktuelle System durch das Entfernen des mit Anmerkungen versehenen Elements viele Rollen übernehmen kann, die es sonst nicht übernehmen würde.

@sgrif Könnten Sie

@ohne Boote 90% davon sind mehr oder weniger das, was Sie erwarten würden. Wir berühren keine vom Benutzer bereitgestellten Elemente. Wir entfernen jedoch kommentierte Elemente, um Knallmakros in das System zu hacken. https://github.com/diesel-rs/diesel/blob/master/diesel/src/macros/macros_from_codegen.rs#L12 -L18. Darüber hinaus berühren wir den Eingabe-Token-Stream nur dann, wenn wir Anmerkungen entfernen. https://github.com/diesel-rs/diesel/blob/master/diesel_codegen/src/lib.rs#L109 -L120

@sgrif Ich habe auch benutzerdefinierte Ableitung missbraucht, um prozedurale Bang-Makros zu erhalten. Ich persönlich würde es sehr bevorzugen, wenn es in Makros 1.1 ein prozedurales Bang-Makrosystem gibt, so dass wir nicht versucht waren, diese Funktion zu sehr zu missbrauchen, weil sie grob ist. Ich denke, ein guter Weg wäre, auch eine nahezu identische prozedurale Knall-Makro-Story in Gang zu bringen (möglicherweise mit etwas hartnäckiger, aber einfacher Hygiene, wie z. B. alle Identifikatoren, die nicht im TokenStream vorhanden sind, der an das Makro übergeben wird, sind mit Hygiene verborgen? I. Ich weiß nicht genau, wie das aussehen würde.) Verwenden Sie dies, anstatt die benutzerdefinierte Ableitung zu missbrauchen, und machen Sie die benutzerdefinierte Ableitung dann unfähig, die Struktur, in der sie sich befindet, zu ändern. Auf diese Weise ermöglichen wir noch mehr Kisten, verbessern den UX und machen die Ableitung gesund.

Ich kann das Argument für die Ableitung jedoch als einfaches Makro verstehen.

Meiner Ansicht nach besteht das Ziel von Makros 1.1 darin, so flexibel wie möglich zu sein, um so viele Anforderungen wie möglich mit geringem Wartungsaufwand zu erfüllen, damit es schnell stabilisiert werden kann und als Notlösung bis Makros 2.0 fungiert. Das aktuelle Design passt meiner Meinung nach sehr gut zu dieser Rolle.

Wenn wir über etwas sprechen würden, das diese Rolle dauerhaft ausfüllen sollte, wäre ich viel mehr dagegen

Vielleicht irre ich mich, aber meine Lektüre des RFC ist, dass dies die Grundlage für das Verhalten der dauerhaften Ableitung sein soll. Das heißt, TokenStream in Zukunft weitere Methoden hinzugefügt, aber Ableitungsmakros werden diese API verwenden, die es ihnen derzeit ermöglicht, beliebige Mutationen für das mit Anmerkungen versehene Element durchzuführen (und diese Fähigkeit wird für den Anwendungsfall von deisel benötigt ).

Ich finde es ziemlich negativ, dass Ableitungen dies dauerhaft tun dürfen. Wenn wir akzeptieren, dass dies ein System ist, das irgendwann zusammen mit macro_rules Makros veraltet sein wird, und unter Makros 2.0 wird eine andere Ableitungs-API bevorzugt, die zurückhaltender ist, bin ich bequemer damit.

Es scheint, dass die Absicht besteht, Dekorateure als Transformatoren zu unterstützen, die alles können.
Und leiten Sie als einfacher Dekorateur ohne Eingabe in das Attribut ausgesetzt.

@withoutboats : serde unterstützt eine Reihe von Attributen , um die Generierung der Impls zu ändern. Daher müssen wir diese unbedingt entfernen oder auf andere Weise ignorieren können, nachdem wir sie verarbeitet haben. Wenn es helfen würde, könnten wir dem Compiler irgendwie eine Liste von Attributen zur Verfügung stellen, die entfernt werden sollten, anstatt dass wir das selbst tun wollen.

@eddyb Ich bin dafür, dass Dekorateure alles können, aber nicht dafür, dass sie (auf lange Sicht) ungezügelte Dekorateure sind.

@erickt Richtig. Ich denke, auf lange Sicht wäre die ideale Lösung, diese Attribute als benutzerdefinierte No-Op-Attribute zu registrieren, anstatt dass der Deriver dafür verantwortlich ist, sie zu entfernen. Aber das ist kurzfristig nicht machbar.

Ich denke, auf lange Sicht wäre die ideale Lösung, diese Attribute als benutzerdefinierte No-Op-Attribute zu registrieren, anstatt dass der Deriver dafür verantwortlich ist, sie zu entfernen. Aber das ist kurzfristig nicht machbar.

Ich bin nicht mit den Compiler-Interna vertraut, die dies kurzfristig unmöglich machen, aber wäre es möglich, dass das Plugin für benutzerdefinierte Ableitungen eine Liste von benutzerdefinierten Attributen anzeigt, die es entfernen möchte, und dann alle anderen Transformationen für die Attribute auf dem ablehnt Originalartikel?

Ich stelle auch fest, dass Diesel nicht dem Ansatz von Serde folgt, alle benutzerdefinierten Attribute unter einem Namen zu haben (z. B. #[serde(rename = "name")] im Gegensatz zu Diesels #[table_name = "name"] .). Würde dies die Implementierung vereinfachen, wenn nur ein einziger benutzerdefinierter Attributname verwendet würde? wurde anstelle einer Liste registriert?

Eine Möglichkeit für das Problem mit den Steuerungsattributen besteht darin, Rückrufe nach der Erweiterung hinzuzufügen, ähnlich wie bei syntex: https://github.com/sfackler/rust-postgres-derive/blob/master/postgres-derive-codegen/src/lib. rs # L23 -L50

Ihre abgeleiteten Impls können unabhängig sein und keine Strip-Control-Attribute, und Sie können einen Durchgang registrieren, der ausgeführt wird, nachdem alle Erweiterungen zur Bereinigung durchgeführt wurden.

Ich habe Rückrufe nach der Erweiterung für Makros 1.1 in post-expansion implementiert. Ihre abgeleiteten Impls können unabhängig sein und keine Strip-Control-Attribute, und Sie können einen Durchgang registrieren, der ausgeführt wird, nachdem alle Erweiterungen für Strip-Attribute durchgeführt wurden.

Wir haben das Problem der Änderung / Bestellung heute auf dem lang-Team-Meeting besprochen. Es bestand der Wunsch, die Erwartungen der Benutzer zu erfüllen und dies zu vereinfachen (in Bezug auf das Design, nicht zu viele Hacks überlagert zu haben und nicht zu schwer zu entschlüsseln / zu debuggen). Es wurde festgestellt, dass eine Änderung der Zieldaten zwar überraschend, aber nicht unsicher ist. Es wurde auch das Gefühl geäußert, dass wir eher um ihrer selbst willen zur Reinheit tendieren als aus gut motivierten Gründen.

Am Ende haben wir entschieden, dass Ableitungen, die die Quelle nicht ändern, wahrscheinlich besser sind, und wir sollten zu diesem Modell wechseln. Das könnte bedeuten, die FCP-Frist zu verlängern. Wir waren nicht der Meinung, dass es einen speziellen Mechanismus für den Umgang mit Stripping-Attributen geben sollte. Die Art und Weise, wie der Compiler mit Attributen umgeht, sollte vielmehr ermöglichen, dass die von einem Makro verwendeten Attribute im Programm verbleiben. RFC 1755 sollte dies berücksichtigen.

Dies verzögert die Stabilisierung einiger benutzerdefinierter abgeleiteter Benutzer. Wir glauben jedoch, dass die meisten Verwendungen von Derivaten (insbesondere diejenigen, die Benutzer von einer stabilen Toolchain fernhalten) keine Attribute verwenden, sodass die meisten Benutzer von Serde beispielsweise bald zu Stable wechseln können. Diejenigen, die Attribute benötigen, brauchen einige Zyklen länger, aber das hat keinen Einfluss auf den allgemeinen Fall.

cc @alexcrichton , @dtolnay , @sgrif , @erickt - Gedanken?

Attribute werden bei Serde und Diesel wahrscheinlich häufiger verwendet, als Sie vorschlagen. Nur aus eigener Erfahrung bin ich mir ziemlich sicher, dass alle meine Programme, die Serde verwenden, Attribute verwenden. Bei Diesel verwende ich definitiv Attribute, und ich denke, es ist erforderlich, um diesel_codegen mitzuteilen, welche Datenbanktabelle der Struktur zugeordnet ist.

Das heißt, es scheint mir die richtige Wahl zu sein, nicht zuzulassen, dass der Brauch die Struktur mutiert. Es vereinfacht nur das Ganze und verhindert viele seltsame Randfälle. Es ist wichtiger, es richtig zu machen, als es schnell zu erledigen. Wenn die Funktion also etwas länger instabil bleiben muss, scheint das auch in Ordnung zu sein.

Es wurde festgestellt, dass eine Änderung der Zieldaten zwar überraschend, aber nicht unsicher ist.

Es ist nicht unsicher, es sei denn, Sie leiten ein unsicheres Merkmal ab.

Meiner Meinung nach besteht einer der Anwendungsfälle von benutzerdefinierten Ableitungen darin, Merkmale, die als unsicher gekennzeichnet sind, auf sichere Weise zu implementieren, z. B. ein Merkmal, das das Layout der Mitglieder der Struktur, in der es implementiert ist, genau beschreiben muss.

Meine asn1-Kiste benötigt auch Attribute für alles andere als triviale Verwendung, sodass ich effektiv warten muss, bis benutzerdefinierte Attribute landen.

Wäre es eine gute Idee, die benutzerdefinierten Attribute rfc in zwei Teile zu teilen?

  1. Ein RFC, der die Notation und den Namespace für benutzerdefinierte Attribute im Allgemeinen bereitstellt (wobei No-Op-Attribute in Stable zulässig sind).
  2. Ein RFC für die Definition und die Semantik beim Ausführen benutzerdefinierter Attributmakros.

Benutzerdefinierte Attributmakros scheinen etwas zu sein, das beim Ausarbeiten viel erfordert. Wenn Sie den RFC in zwei Teile teilen, können Sie möglicherweise früher stabile Attribute für benutzerdefinierte Ableitungspakete bereitstellen.

Dies bedeutet auch, dass Attribute einen Namensabstand haben und beabsichtigt sind (dh eine externe Kiste ist erforderlich, um Attribute für eine Kiste zu verwenden). Ich kann davon ausgehen, dass dies eine gute Sache ist, um zu verhindern, dass zwei benutzerdefinierte Ableitungsmakros denselben Attributnamen verwenden. Eine gute Erwartung wäre hier, den Namen der Kiste zu verwenden, in der sich das Merkmal für die Attribute befindet.

Gibt es einen Grund, warum diese Implementierung nicht die üblichen name! -Makros umfasst? Einfache TokenStream in, TokenStream out für übliche Makros würden sich bei Schädlingen als äußerst nützlich erweisen, bei denen die Kompilierungszeiten für komplexe Grammatiken 30 Sekunden überschreiten.

@dragostis Um die Zusammenfassung des

Extrahieren Sie einen sehr kleinen Teil des heutigen prozeduralen Makrosystems im Compiler, gerade genug, um grundlegende Funktionen wie benutzerdefiniertes Ableiten zu erhalten und eine schließlich stabile API zu erhalten. Stellen Sie sicher, dass diese Funktionen keine Wartungslast für den Compiler darstellen, und versuchen Sie nicht, gleichzeitig genügend Funktionen für das "perfekte Makrosystem" bereitzustellen. Insgesamt sollte dies als ein schrittweiser Schritt in Richtung eines offiziellen "Makros 2.0" angesehen werden.

In der Praxis wird es eine Menge Design und Tests erfordern, um ein prozedurales Makrosystem zu erhalten, das gut funktioniert, genau wie das Verschlusssystem, das wir jetzt erstellt haben. Kisten wie serde und diesel haben schwerwiegende Usability-Probleme ohne eine geeignete benutzerdefinierte Ableitungsfunktion. Lassen Sie uns also eine Stop-Gap-Maßnahme ergreifen, um sie jetzt zu beheben prozedurales Makrosystem. Die syn und quote Kisten sind gute Beispiele dafür.

@keeperofdakeys Ja , hab das verstanden. Ich frage nicht nach einer Makros 2.0-Implementierung, sondern frage mich nur, ob das Hinzufügen eines weiteren Attributs mit einer Belastung verbunden ist, z. B. proc_macro Dies ist eine minimale Implementierung, die das Ableitungsdesign nur für übliche Makros widerspiegelt. Das Beispiel in pest würde einfach einen Parser für ein struct ableiten, nur würde es ihn nicht von struct selbst ableiten, sondern von einer einfachen Grammatik, die zwischen {} . Ich hoffe, ich grabe aber keine toten Diskussionen aus!

@dragostis Ich habe diese Möglichkeit vor angesprochen . Sie können die Antworten dort lesen: https://internals.rust-lang.org/t/pre-rfc-extend-macros-1-1-to-support-foo-style-macros/3921

@nrc

Es wurde auch der Eindruck erweckt, dass wir eher um ihrer selbst willen als aus gut motivierten Gründen zur Reinheit tendieren.

Ein Hinweis dazu: In vielen Fällen, in denen wir schwierige Entscheidungen treffen mussten, wie Parametrizität und Spezialisierung, wäre es schwierig / unmöglich, den relevanten Begriff "Reinheit" statisch sicherzustellen. In diesem Fall ist es jedoch sehr einfach, dies durch Konstruktion zu gewährleisten, und die Beseitigung der Bedeutung der Reihenfolge für Ableitungen scheint für die Benutzer eine ziemlich starke Vereinfachung zu sein.

In Bezug auf die Stabilität könnten wir in Betracht ziehen, derzeit einen instabilen Mechanismus zum Ignorieren von Attributen hinzuzufügen, der in der Praxis stabil wäre (da die API nicht bruchanfällig wäre), selbst wenn die Verwendung noch nachts erforderlich ist. Wir könnten sogar in Betracht ziehen, einen solchen Seitenkanal zu stabilisieren, mit Plänen, ihn zugunsten eines allgemeineren Mechanismus zu verwerfen, sobald er verfügbar ist. Oder wir könnten den Vorschlag von @keeperofdakeys berücksichtigen und schnell auf die allgemeine Attributlösung drängen, die das unmittelbare

Aus meiner Sicht ist es wichtig, dass keines dieser Probleme Makros 1.1 signifikant daran hindert, für Serde allgemein verwendbar und zumindest "de facto" stabil zu sein (dh nicht zu brechen).

@sgrif

Wenn wir über etwas sprechen würden, das diese Rolle dauerhaft ausfüllen sollte, wäre ich viel mehr dagegen

Ich wollte @withoutboats wiederholen und klarstellen, dass die derzeitige Absicht darin besteht, dass die Makros 1.1-API-Oberfläche auch beim unverändert bleibt. Das könnten wir jedoch noch einmal überdenken - wir könnten es uns eher wie die heutigen macro_rules vorstellen, mit der Absicht, es zu verwerfen, sobald das endgültige System vorhanden ist.

Mein Eindruck von der Attributverwendung benutzerdefinierten derive war ähnlich @jimmycuadra ‚s Eindruck , das ist , dass sie ziemlich häufig verwendet. Ich glaube (aber korrigiere mich, wenn ich falsch liege), dass die benutzerdefinierten Attribute für eine Reihe von Diesel-Anwendungsfällen entscheidend waren.

In diesem Sinne bin ich mir nicht 100% sicher, wie diese Attribute am besten aussehen würden. Es wäre eine Schande, Makros 1.1 zu stabilisieren, aber dennoch eine große Anzahl von Benutzern pro Nacht zu lassen (selbst wenn es jede Nacht "defacto stabil" ist), da dies den Zweck der schnellen Stabilisierung von Makros 1.1 in erster Linie etwas zunichte macht. Mit anderen Worten, wenn wir nicht die Mehrheit der Benutzer von Makros 1.1 tatsächlich auf stabiles Rust ziehen, kann das Stabilisieren von Makros 1.1 meiner Meinung nach warten.

Ein Punkt, der mich jedoch nervt, ist, wie wir glauben, dass benutzerdefinierte Attribute auf lange Sicht aussehen und dies mit dem aktuellen Makros 1.1-System rationalisieren werden. Im aktuellen RFC für benutzerdefinierte Attribute müssen Kisten Whitelist-Namespaces von Attributen oben in einer Kiste deklarieren. Dies bedeutet, dass sich die Verwendung eines benutzerdefinierten Attributs in der benutzerdefinierten Ableitung grundlegend von der Verwendung eines benutzerdefinierten Attributs an anderer Stelle unterscheidet. Diese Unterbrechung beunruhigt mich in Bezug auf eine ergonomische und "am wenigsten überraschende" Perspektive, obwohl ich dies auch als Forcierungsfunktion für die Optimierung des RFC sehe (z. B. wenn ich eine Verknüpfung zur serde -Kiste herstelle, kann dies möglicherweise automatisch Serde-Attribute auf die Whitelist setzen ).

Insgesamt wäre ich persönlich in Ordnung, wenn ich das Makros 1.1-System so wie es heute ist vollständig stabilisieren würde. Mit anderen Worten, stabilisieren Sie die Tatsache, dass benutzerdefinierte abgeleitete Erweiterungsfunktionen eine Struktur erhalten und diese dann auch zurückgeben müssen, wenn sie beibehalten werden sollen.

Ich neige dazu, von rustc-serialize zu serde zu wechseln, weil die Codegenerierung von serde Steuerattribute unterstützt.

Wir könnten Makros 1.1 erweitern, um Argumente für das Ableitungsattribut selbst zu unterstützen. Das scheint im Allgemeinen eine nette Sache zu sein, und es könnte missbraucht werden, um das Fehlen von Kontrollattributen kurzfristig potenziell zu umgehen.

Ebenso wie @sfackler sind Attribute für Serde wichtiger als der Kommentar von @nrc . Fast alle Anwendungsfälle ohne Attribute würden von rustc-serialize genauso gut bedient. Wenn wir die Leute jeden Abend davon abhalten wollen, können wir dies tun, indem wir Serde zugunsten von rustc-serialize ablehnen.

Ich habe noch keine Meinung zum richtigen Schritt hier, aber ich weiß, dass ich, wenn wir uns ohne Attribute stabilisieren, dringend in Betracht ziehen würde, Doc-Kommentare zu analysieren, um Attribute herauszuholen. Ich stelle mir vor, dass viele Leute sich nicht mit einem Build-Skript befassen möchten, nur um schreiben zu können:

#[serde(skip_serializing)]

.. Anstatt von:

/// <!-- serde(skip_serializing) -->

@ Tomaka

Meiner Meinung nach besteht einer der Anwendungsfälle von benutzerdefinierten Ableitungen darin, Merkmale, die als unsicher gekennzeichnet sind, auf sichere Weise zu implementieren, z. B. ein Merkmal, das das Layout der Mitglieder der Struktur, in der es implementiert ist, genau beschreiben muss.

Was denkst du über meinen Kommentar hier in dieser Hinsicht? Ich fand das ein zufriedenstellendes Muster.

Ich möchte eines klarstellen:

Ist das Entfernen von benutzerdefinierten Attributen das Wichtigste, was Benutzer mit benutzerdefinierten Ableitungen tun?

Ich weiß, dass es einige Hacks gibt, um foo! im Kontext eines Typs zu erweitern (z. B. @sgrif hat einen solchen Anwendungsfall in Diesel erwähnt, ich denke, @tomaka hat auch einen erwähnt) - wie zentral sind diese Anwendungsfälle ?

Der Grund, den ich frage, ist folgender: Ich sehe Vorteile darin, dass der Ableitungsmechanismus nur eine Liste zusätzlicher Implikationen zurückgibt. Insbesondere bedeutet dies, dass die Bereiche für die Typdeklaration selbst korrekt wären. Das Hinzufügen einer schnellen und fehlerhaften API zum Kontext, mit der Sie der Whitelist im Kontext dieses Typs eine Liste von Attributnamen bereitstellen können, scheint eine einfache Lösung für Attribute zu sein, und wir könnten sie jederzeit ablehnen.

Wenn wir jedoch mehr Anwendungsfälle aktivieren möchten (und ehrlich gesagt sind diese Anwendungsfälle etwas ... überraschender, wenn Sie darüber nachdenken, was man von ableiten erwartet), dann funktioniert dies nicht. In diesem Fall wäre es wahrscheinlich in Ordnung, mich so zu stabilisieren, wie es ist, und zu planen, die API "aus Rohbytes" in Zukunft zugunsten einer besseren Methode zum Schreiben von Ableitungen zu verwerfen (schließlich erwarten wir nicht wirklich, dass dies der Fall ist Verwenden Sie trotzdem Rohbytes, oder Token-Streams?) Wir könnten der API vielleicht einen etwas klobigeren Namen geben =)

@nikomatsakis Das Hauptbedürfnis besteht darin, die Attribute nicht zu entfernen, sondern zu ignorieren. Mir sind keine schwerwiegenden Nachteile bewusst, wenn ich sie ignoriere. Das Bereitstellen einer Whitelist für die Ableitungsfunktion über einen zusätzlichen Parameter zum Hauptattribut sollte beispielsweise für alle praktischen Anforderungen ausreichen, bei denen es sich um echte Ableitungen und nicht um prozedurale Makro-Hacks handelt.

Ja, ich bin zwischen zwei Ansätzen hin und her gerissen, die beide klare Nachteile haben:

  1. Der Ansatz "Einfache Ableitungen": Passen Sie die API so an, dass nur zusätzliche Elemente erzeugt werden. Dadurch ist es für Benutzer immer noch instabil, benutzerdefinierte Attribute als Teil der Ableitung zu verwenden, und schließen Sie das Loch, durch das Diesel und andere Kisten springen, um beliebige Verfahrensmakros zu erhalten.
  2. Der "geplante Veralterungs" -Ansatz - Stabilisierung der API wie sie ist, mit kleinen Anpassungen, wie sie möglicherweise von Niko in Bezug auf die Benennung erwähnt wurden, mit der Absicht, sie eines Tages zu verwerfen. Dies erfordert, dass alle benutzerdefinierten abgeleiteten Autoren eines Tages ihren Code neu schreiben und die Möglichkeit eines überraschenden Verhaltens in der Zwischenzeit berücksichtigen.

EDIT: aber auch die Whitelist von

Ich meine, es ist nicht so, dass @nrc etwas ganz anderes vorschlägt (obwohl IIRC über Whitelist in der Benutzerkiste spricht), und es wird albern, über "Attribute als verwendet markieren" zu sprechen, wenn Sie nur einen Token-Stream haben.

Die willkürlichen prozeduralen Makros, für die ich das Makros 1.1-System in rust-cpp missbraucht habe, wären mit einem alternativen Ansatz durchaus möglich, der einfach die Möglichkeit bietet, Impls hinzuzufügen und Attribute zu ignorieren.

Ich denke, dass die Fähigkeit, Attribute zu ignorieren, wesentlich ist, aber ansonsten wäre es in Ordnung, wenn ich die Struktur nicht über diesen Punkt hinaus ändern könnte.

Ich bin mir nicht sicher, welche Arten von Hacks @sgrif in deisel verwendet, was in einer Welt, in der wir die Struktur selbst nicht ändern können und stattdessen nur zusätzliche Elemente hinzufügen könnten, nicht möglich wäre.

Das Zulassen , dass benutzerdefinierte @sfackler vorgeschlagen , kann eine Möglichkeit sein, jedem die Funktionalität zu

#[derive(Debug, Clone, Serialize(field("bar", rename = "baz")))]
pub struct Foo {
  pub bar: String,
}

Dieses Formular könnte später zugunsten benutzerdefinierter Attribute veraltet sein, sobald eine Entscheidung darüber getroffen wurde.

Sowohl serde als auch meine Kiste benötigen Feld- / Variantenattribute. Der Versuch, dies mit abgeleiteten Argumenten zu emulieren, macht wenig Sinn, wir brauchen Attribute.

Unabhängig davon, welche Entscheidung wir treffen, um dies zu stabilisieren, wäre es schön, wenn die Benutzer von benutzerdefinierten Ableitungsmakros ihren Code nicht ändern müssten, wenn wir zu einer Makros 2.0-Ableitungs-API wechseln (offensichtlich werden es die Autoren von benutzerdefinierten Ableitungsmakros tun). Es scheint die vernünftigste Entscheidung zu sein, dem Compiler eine Liste von Attributen zu geben, die für Ihre Ableitung entfernt werden sollen. Kritisch gesehen werden diese Attribute erst entfernt, nachdem alle Ableitungsmakros ausgeführt wurden. Serde, Diesel und meine Kiste haben alle das Problem, dass dasselbe Attribut für mehrere Ableitungsmakros erforderlich ist.

Mit dem Stripping-Verhalten würde ich die von @dtolnay erstellte Post-Expansion-Kiste nicht benötigen, um ein weiteres Ableitungsmakro hinzuzufügen und Attribute zu

FWIW Der einzige Grund, diese Attribute zu entfernen, besteht darin, das HIR schlanker zu halten. Was alles andere betrifft, müssen Sie sie nur als verwendet markieren.

Ist das Entfernen von benutzerdefinierten Attributen das Wichtigste, was Benutzer mit benutzerdefinierten Ableitungen tun?

Ich weiß, dass es einige Hacks gibt, die man machen muss! Erweiterung im Kontext eines Typs (z. B. @sgrif erwähnte einen solchen Anwendungsfall in Diesel, ich denke, @tomaka erwähnte auch einen solchen) - wie zentral sind diese Anwendungsfälle?

Ich bin völlig in Ordnung mit der Tatsache, dass benutzerdefinierte Ableitungen die Struktur nicht ändern können.

Ich meckere oft über das Fehlen von Plugins in stabilem Rust. Als ich benutzerdefinierte Ableitungen sah, nutzte ich sie als Gelegenheit, Plugins zu haben. Aber ich werde natürlich nicht argumentieren, dass benutzerdefinierte Ableitungen etwas unterstützen müssen, für das sie nicht entwickelt wurden.

Es sieht so aus, als ob es in der letzten Nacht eine Regression gibt. Den Fehler bekommen

Queryable ist ein Ableitungsmodus

bei der Zusammenstellung unserer Beispiele.

#[derive(Queryable)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub published: bool,
}

@sgrif Dies wurde durch # 37198 verursacht, bei dem benutzerdefinierte Ableitungen so geändert wurden, dass sie denselben Namespace wie andere Makros verwenden (sodass bang!() Makros, #[attribute] Makros und #[derive(custom)] Makros gemeinsam genutzt werden der gleiche Namespace).

In diesem Beispiel importiert #[macro_use] extern crate diesel; ein Bang-Makro mit dem Namen Queryable und #[macro_use] extern crate diesel_codegen; importiert ein benutzerdefiniertes Ableitungsmakro mit dem Namen Queryable , das das Bang-Makro stillschweigend überschreibt (beiseite) - #[macro_use] stillschweigendes Überschreiben ist nicht ideal, wird aber kein Problem sein, wenn wir bald Makros aus externen Kisten mit use anstelle von #[macro_use] importieren können!).

Ich glaube, der Fehler wird durch einen Bang-Makro-Aufruf Queryable!(); in der Erweiterung des benutzerdefinierten Derivats verursacht, der in das benutzerdefinierte Derivat von diesel_codegen anstelle des Bang-Makros von diesel .

@jseyfried Was ist der Grund, einen einzigen Namespace für alle drei "Arten" von Makros zu verwenden? Es erscheint mir nicht sinnvoll, Attribute und Bang-Makros so zu behandeln, dass sie denselben Namespace belegen, genauso wenig wie es sinnvoll wäre, Funktionen und Typen so zu behandeln, dass sie denselben Namespace belegen (leiten Sie Makros noch weniger ab).

@withoutboats Ich denke, das ist die falsche Analogie. Ich denke lieber, dass verschiedene Arten von Makros einen Namespace auf dieselbe Weise gemeinsam nutzen wie verschiedene Arten von Werten oder Typen (z. B. Funktionen und Konstanten). Namespaces sind mit Komplexitätskosten verbunden, und ich glaube, je weniger wir haben, desto besser. Die Frage sollte also sein, warum wir unterschiedliche Namespaces benötigen. Aus Gründen der Rückkompatibilität benötigen wir Makros, die sich in einem anderen Namespace als Funktionen und Typen befinden.

Das eigentliche Problem ist, dass Sie use zum selektiven Importieren oder Umbenennen von Makros verwenden können. Ein Kistenbenutzer hat also keine Möglichkeit, Zusammenstöße von Makronamen zu umgehen. Gleichzeitig würde ich nicht erwarten, dass das einfache Importieren einer ProcMacro-Kiste mit lokalen Makronamen kollidiert - ich dachte, die Ableitungsmakros begannen mit derive_ ?

Namespaces sind mit Komplexitätskosten verbunden, und ich glaube, je weniger wir haben, desto besser. Die Frage sollte also sein, warum wir unterschiedliche Namespaces benötigen.

Ich denke, Elemente sollten einen Namespace nur dann gemeinsam nutzen, wenn sie nicht eindeutig sind. consts und fns befinden sich im selben Namespace, da beide als IDents in einem Ausdruckskontext verwendet werden. Typen und Merkmale befinden sich aufgrund der Syntax für Merkmalsobjekte im selben Namespace.

Diese drei Arten von Makros werden alle auf unterschiedliche Weise aufgerufen. Für mich macht es keinen Sinn, dass sie einen Namespace gemeinsam nutzen, da der Aufruf niemals mehrdeutig sein kann.

Obwohl die Komplexität der Implementierung mit Kosten verbunden ist, glaube ich nicht, dass die Komplexität der Verwendung erheblich ist, wenn diese separat benannt werden. Wenn ich über die Komplexitätskosten eines Sprachfeatures nachdenke, denke ich normalerweise über die Komplexität der Verwendung nach.


Würden Sie sagen, dass sich _all_ Elemente im selben Namespace befinden sollten, wenn es sich nicht um eine wichtige Änderung handelt? Versuchen Sie, Ihren Denkprozess zu klären.

Wenn ich ein bisschen mehr darüber nachdenke, kann ich gut sagen, dass es nur einen Namespace geben sollte, in dem sich alle Elemente befinden - ich bevorzuge es sogar irgendwie; Das gesamte Muster der "echten Konstruktoren" ist IMO etwas verwirrend - aber das ist nicht die Entscheidung, die Rust für Nicht-Makro-Elemente getroffen hat. Daher scheint es mir konsistenter zu sein und zu erwarten, dass Makro-Elemente unterschiedliche Namespaces belegen.

Das eigentliche Problem ist, dass Sie mit use keine Makros selektiv importieren oder umbenennen können.

Sie können mit Makros 2.0. Das Modell hier ist im Grunde ein Stop-Gap-Hack.

Ich dachte, die Ableitungsmakros begannen mit derive_?

Das war das alte benutzerdefinierte Ableitungssystem, ich glaube nicht, dass Makros 1.1 dies tun.

Die Komplexität der Implementierung ist zwar mit Kosten verbunden, die Komplexität der Nutzung ist jedoch meines Erachtens nicht mit erheblichen Kosten verbunden

Es gibt Kosten für eine Definition der Verwendung - dies erschwert insbesondere den Werkzeugen das Leben (obwohl man nicht viel argumentieren könnte). Ich denke auch, dass es nicht so sehr auf die Komplexität der Implementierung ankommt (obwohl Namespaces den Compiler definitiv komplizieren, stimme ich zu, dass dies nicht so wichtig ist), sondern vielmehr auf die Komplexität der Sprache - Benutzer müssen über dieses Zeug nachdenken, um Rust zu verwenden, und es hat einen Einfluss Auswirkungen auf Dinge wie Hygiene und Beschattung, was die Dinge noch komplizierter macht.

Würden Sie sagen, dass sich alle Elemente im selben Namespace befinden sollten, wenn dies keine bahnbrechende Änderung wäre? Versuchen Sie, Ihren Denkprozess zu klären.

Ich würde nicht so weit gehen - ich denke, dass es Vorteile hat, wenn Werte und Typen getrennt sind, aber sicherlich wäre es viel schöner, mit einer Sprache mit einem einzigen Namespace in vielerlei Hinsicht zu arbeiten.

Aber das ist nicht die Entscheidung, die Rust für Nicht-Makro-Elemente getroffen hat

Kontrapunkt: Module und Felder befinden sich im Wert-Namespace, obwohl sich die Orte, an denen sie verwendet werden können, (glaube ich) von den Orten unterscheiden, an denen andere Werte verwendet werden können.

@keeperofdakeys

Das eigentliche Problem ist, dass Sie use zum selektiven Importieren oder Umbenennen von Makros verwenden können

In Kürze (~ 1 Woche) können wir use Makros von extern crate hinter einem Feature-Gate erstellen, siehe https://github.com/rust-lang/rfcs/pull/1561 und # 35896. Wir könnten uns entscheiden, use Makros aus benutzerdefinierten Ableitungskisten zusammen mit benutzerdefinierten Ableitungen selbst zu stabilisieren.

Ich dachte, die Ableitungsmakros begannen mit derive_

Dies galt für benutzerdefinierte Ableitungen im alten Stil. Bei benutzerdefinierten Ableitungen von Makros 1.1 erwartet #[derive(Serialize)] (zum Beispiel), dass Serialize in ein benutzerdefiniertes Ableitungsmakro aufgelöst wird.

@ohne Boote

Was ist der Grund, einen einzigen Namespace für alle drei "Arten" von Makros zu verwenden?

  • Vorrang: Bang-Makros und Attribut-Makros haben einen Namespace schon lange gemeinsam genutzt. Bis zu diesen Änderungen sollten benutzerdefinierte Ableitungen diesen Namespace auch gemeinsam nutzen.
  • Abwärtskompatibilität: Wenn wir mit einem einzelnen Makronamensraum beginnen, können wir ihn abwärtskompatibel aufteilen. Wenn wir mit mehreren Makronamensräumen beginnen, bleiben wir für immer bei ihnen.
  • Einfachheit: Wenn jede Makroart einen eigenen Namespace hätte, hätten wir insgesamt fünf Namespaces. Nachdem https://github.com/rust-lang/rfcs/pull/1561 gelandet ist, kann use foo::bar; fünf separate Elemente mit dem Namen bar (ein Wert, ein Typ, ein Knallmakro) importieren usw.), und es gibt keine einfache Möglichkeit, beispielsweise das Bang-Makro erneut zu exportieren, aber nicht das benutzerdefinierte Ableitungsmakro, oder das benutzerdefinierte Ableitungsmakro einfach als baz zu importieren.

Abwärtskompatibilität: Wenn wir mit einem einzelnen Makronamensraum beginnen, können wir ihn abwärtskompatibel aufteilen. Wenn wir mit mehreren Makronamensräumen beginnen, bleiben wir für immer bei ihnen.

Dies ist überzeugend, zumal 1.1 eine Notlösung sein soll. : +1:

Ist dieser Unterbrechungscode für jemanden geeignet, der ein Makro_rules-Makro mit dem Namen PartialEq! ?

Nein, PartialEq ist heute im Makronamensraum nicht definiert, da es sich nicht um eine benutzerdefinierte Ableitung handelt.
#[derive(Foo)] prüft zuerst, ob Foo eine "eingebaute Ableitung" ist ( PartialEq , Copy usw.), bevor nach einer benutzerdefinierten Ableitung gesucht wird Foo im Makronamensraum. Die eingebauten Elemente sind in der Definition von derive fest codiert.

Abgesehen davon könnten wir Code brechen, wie Sie es beschrieben haben, wenn wir uns schließlich dazu entschließen, PartialEq im Vorspiel zu einer benutzerdefinierten Ableitung zu machen, anstatt zu einem eingebauten. Daher ist es möglicherweise eine gute Idee, dies jetzt zukunftssicher zu machen.

Vorschlag für das Attributproblem (vorausgesetzt, wir arbeiten an einem Modell, bei dem Ableitungsmakros die Elemente, für die sie deklariert sind, nicht ändern, sondern nur dekorieren können):

  • Hinter den Kulissen implementieren benutzerdefinierte Ableitungsmakros ein Merkmal, derzeit MultiItemModifier . Es ist geplant, dass Makros 2.0 weiterhin ein Merkmal implementieren und dieses Merkmal zur Erweiterbarkeit des Mechanismus verwendet wird. Die mit Anmerkungen versehene Funktion des Makros implementiert dieses Merkmal. Obwohl wir den Plugin-Registrar nicht verwenden, ist dies im Wesentlichen ein Desugaring.
  • Wir sollten ein CustomDerive Merkmal speziell für die benutzerdefinierte Ableitung von Makros 1.1 aufteilen. Im allgemeinen Fall sehen Makroautoren es nie. Sie haben jedoch die Möglichkeit, das Merkmal direkt zu implementieren, anstatt ein Attribut für eine Funktion zu verwenden (ich gehe davon aus, dass wir das Attribut für das Impl wiederverwenden würden, möglicherweise müssen wir diesen Registrierungsmechanismus diskutieren).
  • wir fügen Sie eine declare_attributes Funktion CustomDerive , welche zurückgibt Vec<String> . Es gibt ein Standardimplement, das einen leeren VEC zurückgibt.
  • Wenn Makroautoren diese Methode implementieren, werden alle Attribute, die genau als eine der zurückgegebenen Zeichenfolgen benannt sind, als zum Makro gehörend betrachtet. Ein solches Attribut wird niemals wie ein Makro nachgeschlagen und löst keine benutzerdefinierten Attributflusen aus. Attribute werden durch Ableiten der Erweiterung im Code belassen, jedoch als verwendet markiert. Jedes solche Attribut, das nicht von einer Ableitungserweiterung berührt wird, würde weiterhin die nicht verwendeten Attributflusen auslösen (jedoch nicht die benutzerdefinierten Attributflusen).
  • Wir könnten in Zukunft Attribute mit Gültigkeitsbereich einführen, die von Makros einschließlich benutzerdefinierter Ableitungen verwendet werden können. Diese würden zusätzlich zu declare_attributes und dieser Mechanismus würde nicht veraltet sein.
  • Alternative: Die von declare_attributes Zeichenfolgen werden als Pfadpräfix für Attribute überprüft, z. B. wenn declare_attributes vec!["foo::bar"] , dann #[foo::bar::baz] und #[foo::bar::qux] wäre erlaubt.

Gedanken? cc @alexcrichton @jseyfried @dtolnay @sgrif @erickt @ rust-lang / lang

@nrc würde dieser Mechanismus erlauben, _used_ Attribute wie #[cfg(..)] zu deaktivieren - entweder aus Versehen oder absichtlich? Und könnte das geändert werden?

@nrc Leider kann ich nicht genau sehen, wie die Implementierung angesichts dieser Einschränkungen aussehen würde, daher bin ich mir nicht ganz sicher, ob es funktionieren würde oder nicht.

Davon abgesehen bin ich etwas skeptisch, ob Merkmale und Merkmalsobjekte funktionieren würden, denn wo werden die Instanzen des Merkmals erstellt?

@nrc Warum muss dies zwingend sein und kann nicht nur eine Liste im Attribut sein, das die Derivw-Expander-Funktion angibt? ZB #[proc_macro_derive(Serialize, uses_attrs(serde_foo, serde_bar))] oder so.

@ colin-kiegel Ich kann mir vorstellen, dass dies nur im Rahmen der Ableitungsanwendung gelten kann. In diesem Fall ist das Deaktivieren anderer Attribute wahrscheinlich ein erwartetes Verhalten, obwohl ich denke, dass wir cfgs möglicherweise vor der Makroerweiterung anwenden (dies hat sich geändert, aber ich kann mich nicht erinnern das vorher gegen nachher).

@alexcrichton

Davon abgesehen bin ich etwas skeptisch, ob Merkmale und Merkmalsobjekte funktionieren würden, denn wo werden die Instanzen des Merkmals erstellt?

Hmm, guter Punkt, ich nehme an, wir müssen dies für Makros 2.0 ansprechen

@eddyb Dies ist eine gute Idee und wahrscheinlich kurzfristig einfacher zu machen. Mein Grund, den imperativen Ansatz zu bevorzugen, ist, dass es sich um einen allgemeinen Erweiterungsmechanismus handelt, den wir beibehalten möchten, während das Hinzufügen von Dingen zum Attribut nicht allzu gut skaliert und möglicherweise nicht für immer beibehalten werden soll . Auf der anderen Seite ist es jetzt sicherlich einfacher und es scheint keine schlechte Sache zu sein, herumzuhängen, also denke ich, dass es eine bessere Wahl sein könnte.

Aufgefangen werden - ich war außer Landes und dann krank. Ich möchte https://github.com/rust-lang/rust/pull/37198 erneut besuchen, da ich es nicht für sinnvoll halte, dass alle Arten von Makros denselben Namespace belegen. Ich würde zumindest erwarten, dass benutzerdefinierte Ableitungsmakros in diesem Namespace als derive_Foo oder ähnlich vorliegen. Es gibt Anwendungsfälle für mehrere Arten von Makros, die denselben Namen auch in der Standardbibliothek verwenden (z. B. #[cfg] und cfg! ).

Ich finde auch den gemeinsamen Namespace etwas umständlich. Aus Sicht des Endbenutzers könnte es so aussehen, als ob eine Art Austauschbarkeit, Mehrdeutigkeit oder etwas Interessantes vor sich geht - was es nicht gibt. Ich denke, solche 'zufälligen' Einschränkungen könnten das Verständnis von Rust als Sprache etwas erschweren.

Ich denke jedoch, dass es wahrscheinlich nicht das Ende der Welt ist. Es scheint, dass in Zukunft noch verschiedene Namespaces eingeführt werden könnten, ohne zu viel zu brechen.

@sgrif @ https://github.com/rust-lang/rust/issues/35900#issuecomment -256247659 beschrieben.

Ich halte es nicht für sinnvoll, dass alle Arten von Makros denselben Namespace belegen

Um es klar auszudrücken, haben Bang-Makros und Attribut-Makros immer denselben Namespace belegt. # 37198 hat gerade benutzerdefinierte Ableitungen in diesen Namespace verschoben.

Es gibt Anwendungsfälle für mehrere Arten von Makros, die denselben Namen auch in der Standardbibliothek verwenden (z. B. #[cfg] und cfg! ).

Alternativ könnte cfg als ein einzelnes Makro angesehen werden, das über einen Bang-Aufruf oder einen Attributaufruf verwendet werden kann (während Benutzer heute kein Makro definieren können, das über einen Bang oder ein Attribut aufgerufen werden kann). wir könnten uns dafür entscheiden, es in Makros 2.0) zuzulassen.

Aus Sicht des Endbenutzers könnte es so aussehen, als ob es eine Art Austauschbarkeit gäbe

Ich denke, dies könnte durch eindeutige Fehlermeldungen verbessert werden, wenn zwei Makros in Konflikt stehen (ich werde daran arbeiten, die heutigen Meldungen zu verbessern).

Es scheint, dass in Zukunft noch verschiedene Namespaces eingeführt werden könnten, ohne zu viel zu brechen

Ich glaube, dass das Aufteilen des Makronamensraums vollständig abwärtskompatibel ist (korrigieren Sie mich, wenn ich falsch liege). Dies ist meine Hauptmotivation, heute einen einzigen Makronamensraum beizubehalten - wir möchten, dass Makros 1.1 so zukunftskompatibel wie möglich sind.

Schließlich denke ich, dass Konflikte zwischen Bang / Attribut-Makro und benutzerdefinierten Ableitungsmakros in der Praxis selten sind, da Bang / Attribut-Makros normalerweise in Kleinbuchstaben und benutzerdefinierte Ableitungen normalerweise in Großbuchstaben geschrieben werden. Mit anderen Worten, benutzerdefinierte Ableitungen haben bereits einen eigenen Namespace, wenn diese Namenskonventionen befolgt werden.

Es sieht so aus, als ob der Bruch (https://github.com/diesel-rs/diesel/issues/485) durch Unterstützung verursacht wird, z. B. Queryable! { struct S; } sowie #[derive(Queryable)] struct S; . Sobald benutzerdefinierte Ableitungen stabil sind, muss Queryable! { struct S; } nicht mehr unterstützt werden, sodass dies kein Problem mehr darstellt, oder?

Ich glaube, wir könnten in der Zwischenzeit diesel aktualisieren, damit

  • Queryable! wird weiterhin ohne #[macro_use] extern crate diesel_codegen; und unterstützt
  • #[derive(Queryable)] , aber nicht Queryable! , wird mit #[macro_use] extern crate diesel_codegen; .

Sie können mich gerne im IRC anrufen, um darüber zu diskutieren - ich würde gerne eine PR schreiben.

@nrc @alexcrichton

Davon abgesehen bin ich etwas skeptisch, ob Merkmale und Merkmalsobjekte funktionieren würden, denn wo werden die Instanzen des Merkmals erstellt?

Ich stimme @eddyb zu, dass wir, wenn wir nur Attribute behandeln wollen, im Interesse der Bequemlichkeit und Zweckmäßigkeit nur die Attribute erweitern sollten.

Aber wenn wir ein flexibleres Erweiterungssystem wollten, dann hatte ich mir vorgestellt, wir würden Merkmale verwenden, aber diese Merkmale würden als eine Sammlung von Methoden definiert, die sich nicht auf Self beziehen:

trait CustomDerive {
     fn foo(); // note: no self
     fn bar(); // again, no self
}

Sie würden es dann auf einem Dummy-Typ wie struct my_annotation_type; implementieren (der vermutlich den gleichen Namen wie das Attribut hat?), Und wir würden die Auflösung von Merkmalen verwenden, um die relevanten Funktionen wie <my_annotation as CustomDerive>::foo (wahrscheinlich) zu extrahieren Beim Schreiben der Metadaten denke ich). Der Punkt ist, wir würden niemals eine Instanz von my_annotation erstellen (oder brauchen), es ist nur ein Gruppierungsmechanismus, um eine Reihe verwandter Funktionen zusammenzufassen.

Sicher nicht das eleganteste überhaupt, aber ich bin mir nicht sicher, wie ich es besser machen soll? Ich denke, die Uneleganz ist genau der Grund, warum wir mit zugeschriebenen Funktionen beginnen wollten. =)

In Bezug auf Namespaces macht @sgrif mit den Beispielen #[cfg] und cfg! einen guten Fall. Ich kann mir sicher vorstellen, dass jemand #[derive(SomeTrait)] möchte und auch ein Makro wie SomeTrait! { ... } , das ... etwas tut. =) Aber @jseyfried macht auch einen guten Fall mit Abwärtskompatibilität - solange wir nicht bereits auf Einschränkungen stoßen.

Ich bevorzuge standardmäßig weniger Namespaces, hauptsächlich wegen des Schmerzes, den ich denke, dass uns eine Aufteilung der Namespaces zwischen Wert und Typ jetzt bringt. Trotzdem denke ich, dass die meisten bekannten Schmerzpunkte hier nicht zutreffen:

  • Die Aufteilung von Typ und Wert ist ein Problem bei der Namensauflösung, da use foo::bar möglicherweise ein Modul mit dem Namen bar importiert oder nicht und daher möglicherweise (oder möglicherweise nicht) für eine Namensauflösung wie bar::baz relevant ist
  • Die Aufteilung von Typ und Wert ist für Generika eine Art Schmerz über Konstanten, obwohl dieser Schmerz auch auf die syntaktische Aufteilung zwischen Typen und Werten zurückzuführen ist.

Aber seit wir die Brücke überquert haben, bin ich mir nicht sicher, ob es eine besondere Herausforderung ist, "benutzerdefinierte Ableitung" in einem eigenen Namespace zu haben, oder?

Wir könnten den generierten Makros das Präfix derive_ hinzufügen, um sie in Pseudo-Namespaces zu bringen. Wenn wir diese Änderung vornehmen wollten, wäre jetzt die Zeit gekommen.

@keeperofdakeys Wäre dies nicht gleichbedeutend mit Autoren von benutzerdefinierten Ableitungen, die sich entscheiden, ihre benutzerdefinierten Ableitungen zu benennen derive_* ? Unabhängig davon denke ich, dass derive_* Namen für Endbenutzer zu hässlich sind.

@ Nikomatsakis

Aber seit wir die Brücke überquert haben, bin ich mir nicht sicher, ob es eine besondere Herausforderung ist, "benutzerdefinierte Ableitung" in einem eigenen Namespace zu haben, oder?

Der Importauflösungsalgorithmus kann beliebig viele Namespaces verarbeiten, erledigt jedoch für jeden Namespace einen möglicherweise nicht trivialen Arbeitsaufwand. Insbesondere für jeden Import I und jeden nicht verwendeten Namespace S beweist dies, dass die Auflösung I in S fehlschlägt (vgl. Https: // github. com / rust-lang / rfcs / pull / 1560 # issuecomment-209119266). Dieser Beweis erfordert häufig eine DFS des Glob-Importdiagramms (in dem Scheitelpunkte Module und Kanten Glob-Importe sind), um nach relevanten unbestimmten Importen zu suchen.

Diese zusätzliche Arbeit pro Namespace macht jedoch in der Praxis möglicherweise keinen Unterschied und kann bei Bedarf vermieden werden (vgl. Https://github.com/rust-lang/rfcs/pull/1560#issuecomment-209119266) geringfügige Einschränkung, die nur für die Makronamensräume gelten würde.

Ich habe die Namespaces in # 37198 zusammengeführt, um unsere Optionen größtenteils offen zu halten, und weil ich nicht dachte, dass dies in der Praxis einschränkend sein würde. Wenn die Leute es heute wollen und @ rust-lang / lang es in Ordnung ist, für immer mehrere Makronamensräume zu haben, habe ich keine Einwände.

@ Nikomatsakis

Ich denke ja, deine Strategie würde funktionieren, wenn auch wackelig. Mein persönliches Gefühl ist, dass die Seltsamkeit nicht an Gewicht gewinnt (z. B. was wir heute haben, ist für mich in Ordnung), aber es sollte auf jede Art und Weise umsetzbar sein.

@alexcrichton, wie ich glaube, ich habe schon früher geschrieben, ich bin froh zu warten, bis wir mehr Kraft brauchen (wenn überhaupt) - und dann können wir so etwas wie das von mir beschriebene Merkmal oder vielleicht etwas Besseres tun. Im Moment denke ich, dass es ausreichen würde, nur die Attribute zu erweitern, die auf ein fn angewendet werden.

Ich bin neugierig geworden und habe eine grundlegende Implementierung dieser Idee gestartet (unter Verwendung eines Attributs in der Funktion proc_macro, um die Namen der Attribute zu kennzeichnen, die als für das Element verwendet markiert werden sollen).

Ich habe das FCP-Tag gelöscht, weil es klar ist, dass wir den Punkt, an dem wir zur Stabilisierung bereit sind, noch nicht ganz erreicht haben. Ich werde mich bemühen, den Stand des Gesprächs zusammenzufassen und insbesondere die Fehler hervorzuheben, bei denen wir noch feste Entscheidungen und / oder Codebeiträge benötigen:

Frage 1: Sollten benutzerdefinierte Makros in der Lage sein, das Objekt, über das sie laufen, zu mutieren?

  • Niemand denkt, dass sie das natürlich in der Praxis tun sollten
  • Es ist ein anderer Modus, der ursprünglich im Geiste von YAGNI abgelehnt wurde
  • Ableitungen, die den Satz von Feldnamen usw. ändern, lassen sich nicht gut zusammensetzen, wodurch die Reihenfolge der Anwendung sichtbar wird.
    Die Gefahr davon wird durch zwei Dinge gemindert:
  • Auf der anderen Seite, wenn wir sagen, dass benutzerdefinierte Ableitung nur die neuen Impls zurückgeben muss

    • Die Span-Informationen sind besser

    • Benutzerdefinierte Ableitungen sind einfacher zu schreiben

  • _Aber viele benutzerdefinierte Ableitungen verwenden benutzerdefinierte Attribute, um ihre Erweiterung zu steuern, und diese generieren Warnungen / Fehler

    • Gegenwärtige Technik besteht darin, sie aus dem AST zu entfernen

  • _Auch: _ wir wollen dieses Zeug so schnell wie möglich in die Welt bringen, wollen nicht lange experimentieren oder drastische Änderungen vornehmen

Vorschläge:

  • Behalten Sie alles so wie es ist, vielleicht veralten Sie es später

    • Zweckmäßig, etwas unglücklich

  • Erweitern Sie #[proc_macro] mit einer Whitelist von Attributen, weisen Sie rustc an, sie als "verwendet" zu betrachten und sie zu ignorieren

Frage 2: Sollten benutzerdefinierte Ableitungen denselben Namespace wie andere Makros haben?

Argument für:

Gegenargument:

Vorschläge:

  • Aufteilen in den Namespace "Benutzerdefinierte Ableitung"
  • Behalten Sie den Status Quo

Andere Dinge?

Gibt es noch offene Fragen?

Mögliche Lösung für das Mutationsproblem:

Anstelle von benutzerdefinierten Ableitungen mit dem Typ TokenStream -> TokenStream hätten sie stattdessen den Typ
Item -> TokenStream , wobei Item ein undurchsichtiger Typ ist, der mit nur zwei Methoden beginnen würde:

  • item.tokens() , das die TokenStream zurückgibt, die heute übergeben würden, und
  • item.use_attrs(name) , wodurch alle Attribute mit dem angegebenen Namen als verwendet markiert werden.

Die zurückgegebenen TokenStream würden nur die abgeleiteten impl s enthalten.
Wir könnten schließlich die API von Item mit praktischen Funktionen (z. B. Durchlaufen der Felder / Varianten des Elements) oder einer übergeordneten Ableitungs-API wie der in syntax_ext::deriving::generic ergänzen.

Ich würde gerne implementieren, dass Attribute auf der weißen Liste in #[proc_macro_derive] (dh der zweite Vorschlag für Frage 1 in https://github.com/rust-lang/rust/issues/35900#issuecomment-258315395) oder Mein Item -> TokenStream Vorschlag.

Ich denke, es wäre ein Fehler, benutzerdefinierte Ableitungen zu stabilisieren, die das zugrunde liegende Element mutieren können. Die Spanneninformationen, die wir durch das Zulassen von Mutationen opfern, verursachen bereits Probleme - zum Beispiel müssen wir zwischen Fixing # 37563 und Fixing # 36218 wählen.

Ich sehe den Reiz einer zwingenden Whitelist wirklich nicht. Kann sich jemand einen Anwendungsfall einfallen lassen?

Ich bin nicht sicher, ist es beabsichtigt / erwünscht, dass dieser Code kompiliert wird:

#![feature(proc_macro)]

#[proc_macro_derive(Whatever)]
struct Foo {}

@eddyb Ich bin mir nicht sicher, ob es einen inhärenten Reiz der imperativen Whitelist gibt - ich denke, der Vorteil von
Item -> TokenStream ist, dass es einfacher ist, die API von Item , als mehr Arten von deklarativen Attributen hinzuzufügen. Abgesehen davon gibt es möglicherweise nicht viele andere Anwendungsfälle, die ein Item über ein TokenStream erfordern, also TokenStream -> TokenStream mit einer deklarativen Whitelist könnte besser sein.

Werden Makros 2.0 diese API ersetzen? In diesem Fall spielt die Erweiterbarkeit keine Rolle, und die API Item -> TokenStream scheint nicht sehr überzeugend zu sein.

@keeperofdakeys Laut RFC:

Insgesamt sollte dies als ein schrittweiser Schritt in Richtung eines offiziellen "Makros 2.0" angesehen werden.

Die Absicht von Makros 1.1 ist es, in Geist und Implementierung so nah wie möglich an Makros 2.0 zu sein, ohne große Mengen von Funktionen zu stabilisieren. In diesem Sinne ist es die Absicht, dass wir bei stabilen Makros 1.1 Features abwärtskompatibel überlagern können, um zu Makros 2.0 zu gelangen.

https://github.com/rust-lang/rfcs/pull/1681#issuecomment -233449053 und https://github.com/rust-lang/rfcs/pull/1681#issuecomment -233708395 und https: // github. com / rust-lang / rfcs / pull / 1681 # issuecomment -239558657 scheint darauf hinzudeuten, dass nur "begrenzte" Abwertungen erwartet werden, wenn Makros 2.0 eintreffen. Insbesondere: "Das Muster von String-ifying-Token-Bäumen würde nicht bevorzugt, wenn es nicht tatsächlich veraltet wäre".

@eddyb

Ich sehe den Reiz einer zwingenden Whitelist wirklich nicht. Kann sich jemand einen Anwendungsfall einfallen lassen?

Nun, ich könnte mir einen Fall vorstellen, in dem die Namen der Attribute auf irgendeine Weise dynamisch generiert werden (vielleicht basierend auf dem Namen des Typs oder der Felder?) - und daher kann man sie nicht im Voraus deklarieren. Scheint allerdings künstlich und keine besonders gute Praxis zu sein.

Ich finde die Alternative von @jseyfried ansprechend - nicht wegen der Item möglicherweise umfangreicher wird. Das heißt, wir unterstützen möglicherweise das strukturierte Durchlaufen der Felder, ermöglichen den Zugriff auf mehr Daten (z. B. Namensauflösungen), die uns möglicherweise zur Verfügung stehen usw.

Natürlich wird ein Teil davon auch (zufällig) von irgendeiner Form der Standard-AST-API stammen.

Ein Hinweis zum Timing: Ich möchte wirklich, dass Makros 1.1 im nächsten Zyklus stabilisiert werden. Das Zeug, für das wir blockiert sind, fühlt sich letztendlich ziemlich unbedeutend an.

In diesem Sinne nehme ich die von mir beschriebenen Themen auf:

  1. Ich bin entweder mit einer deklarativen #[proc_macro] -Erweiterung _oder_ zufrieden, die den Typ in Item ändert. Ich denke auch, dass wir, egal für was wir uns entscheiden, in Zukunft möglicherweise den anderen übernehmen könnten, wenn es eine gute Idee wäre. Ich möchte mit dem gehen, was zuerst implementiert wird. =)
  2. In Bezug auf die mehreren Namespaces denke ich, dass wir sie vorerst im selben Namespace belassen sollten. Für mich ist die Kombination aus Abwärtskompatibilität (dh Offenhalten unserer Optionen) und der Tatsache, dass die Großschreibung bereits "benutzerdefinierte Ableitungs" -Makros von anderen Dingen in der Praxis unterscheidet, überzeugend. Die Unterscheidung zwischen #[cfg] und cfg! fühlt sich wie etwas an, das wir auf andere Weise handhaben könnten, oder wenn wir wollen, können wir die geteilten Namespaces _then_ einführen. Es scheint nicht so, als würde ein einheitlicher Namespace insbesondere dem Anwendungsfall der benutzerdefinierten Ableitung schaden. Dies ist das einzige, worüber wir ohnehin sprechen. Recht?

@nikomatsakis Ihre Zusammenfassung klingt genau, danke für das Schreiben! Ich bin traurig, dass wir in Rust 1.14 keine Makros 1.1 bekommen, aber ich verstehe, dass dies strittige Themen sind.

Mein persönliches Gefühl bleibt es, alles so zu stabilisieren, wie es ist, wo benutzerdefinierte Ableitungen Attribute entfernen müssen und es nur einen Namespace gibt.

Ich folge der Item -> TokenStream API nicht ganz. Umfasst der zurückgegebene Token-Stream immer noch das ursprüngliche Element oder nur die hinzugefügten Impls? Bedeutet das, dass es &mut Item dauern sollte?

@ est31 dein Kommentar klingt wie ein Fehler, kannst du dafür ein separates Problem eröffnen?

Ich stimme dem Kommentar von @nikomatsakis ziemlich vollständig zu. Ich denke, es ist wichtig, dass Ableitungen keine freie Hand haben, um den Gegenstand, an den sie gebunden sind, zu mutieren, aber alle vorgeschlagenen Implementierungen scheinen mir in Ordnung zu sein.

Es scheint nicht so, als würde ein einheitlicher Namespace insbesondere dem Anwendungsfall der benutzerdefinierten Ableitung schaden. Dies ist das einzige, worüber wir ohnehin sprechen. Recht?

Das Problem trat auf, weil der Diesel defekt ist. Derzeit gibt es Makros mit dem Namen Queryable! die Sie eine Struktur einwickeln können, um Queryable dafür abzuleiten.

Als jemand, der derzeit weder Diesel verwendet noch wartet (dh als jemand, der nicht von dem betroffen ist, was ich vorschlagen werde: heat_smile :), denke ich, dass es das Beste ist, vorerst einen Namespace beizubehalten und Diesel den Namen zu ändern diese Makros zu derive_Queryable! oder so ähnlich. Sie werden veraltet sein, wenn dies sowieso stabil ist, oder?

Ich habe PR https://github.com/rust-lang/rust/pull/37614 für die vorgeschlagene Funktion erstellt. Es wird ein separates Attribut #[proc_macro_attributes(..)] , und Sie müssen das Element in dem zurückgegebenen TokenStream nicht mehr zurückgeben.

Ich habe # 37637 eingereicht, um herauszufinden, wie Proc-Makros $crate .

Wird dieser Anwendungsfall zur Verdeutlichung als in Ordnung oder als Missbrauch des Systems angesehen:

Ich generiere neue Strukturen basierend auf den vorhandenen Strukturen und Attributen hier und ich mag das Design sehr, da es mir ermöglicht, Dinge in einer Struktur zu konsolidieren.

Wenn sich das System jedoch später ändert, um diese Art der Implementierung nicht zuzulassen, höre ich möglicherweise jetzt auf, anstatt mehr Aufwand zu betreiben (die aktuelle Implementierung ist nur ein schneller Proof of Concept).

@ TheNeikos

Die wichtigste rückwärts inkompatible Änderung besteht darin, dass Sie das Element nicht mehr ändern können (Sie geben es nicht im TokenStream zurück). Ich würde sagen, dass das Ableiten von etwas anderem als einem Gerät nicht beabsichtigt ist, aber nichts hindert Sie daran, dies zu tun. Sie müssen nur vorsichtig mit Namenskonflikten sein.

Die andere wichtige Änderung besteht darin, eine Liste von Attributnamen bereitzustellen, die keine benutzerdefinierten Attributfehler für das Element auslösen sollten.

RE: Der Namespace-Konflikt in Diesel. Ich bin mir nicht sicher, ob ich diese Makros ablehnen werde oder nicht, sobald diese Funktion stabil ist. Es hängt davon ab, ob einige noch den Wunsch nach Compiler-Erweiterungs-freiem Material haben. Es ist zweifelhaft. Immer noch nützlich für interne Tests, aber das Umbenennen ist in Ordnung.

Ich finde es bedauerlich, die Möglichkeit zu verlieren, den Eingabestream zu ändern. Wenn wir das mit Anmerkungen versehene Element entfernen können, können wir auch mit diesem System Knallmakros erstellen. Ich verstehe die Bedenken, aber es ist eine Schande, diese Fähigkeit über sie zu verlieren.

@TheNeikos @sgrif Mein Standpunkt ist, dass alles, was die Eingabe ernsthaft verändert, keine Ableitung ist und daher hier nicht angesprochen werden soll. Ich denke, es ist etwas gefährlich (mangelnde Hygiene usw.), benutzerdefinierte Ableitungen für allgemeine Prozessmakros zu verwenden, daher bin ich nicht daran interessiert, dies zu fördern.

Wenn das Element nicht geändert werden kann, bleiben die Bereichsinformationen des Elements erhalten, wodurch Fehlermeldungen auf dem Element viel deutlicher werden (dies bedeutet auch, dass Fehlermeldungen in der abgeleiteten Ausgabe nicht mehr auf den Bereich des Elements verweisen, sondern auf den Bereich des Ableitungsattributs). Ich kann keinen guten Grund dafür sehen, dass Leute diese Funktion missbrauchen, wenn wir wirklich nur richtige prozedurale Makros wollen.

@TheNeikos Ich glaube nicht, dass wir das Generieren neuer Strukturen durch eine Ableitung nicht zulassen werden, solange Sie die Struktur, für die Sie ableiten, nicht motivieren.

In Bezug auf das, was ich für idiomatisch halte; Ich denke, es ist in Ordnung, neue Typen zu generieren, aber es ist viel besser, wenn diese Typen assoziierte Typen für ein Merkmal sind, das Sie ableiten. Stellen Sie sich zum Beispiel vor, wir könnten IntoIterator für einen Typ ableiten - dazu müsste eine Iterator -Struktur erstellt werden. Konzeptionell sollten Sie Verhalten für diesen Typ ableiten, aber dieses Verhalten ist möglicherweise kein "implizites Merkmal".

@sgrif

Ich finde es bedauerlich, die Möglichkeit zu verlieren, den Eingabestream zu ändern. Wenn wir das mit Anmerkungen versehene Element entfernen können, können wir auch mit diesem System Knallmakros erstellen. Ich verstehe die Bedenken, aber es ist eine Schande, diese Fähigkeit über sie zu verlieren.

Hmm, ich bin definitiv sympathisch, obwohl dies offensichtlich (wie @nrc bemerkte) nicht das ist, wofür das System entwickelt wurde, und es wird dazu neigen, die verschiedenen rauen Kanten freizulegen. Dies spielt in Ihren Anwendungsfällen wahrscheinlich keine Rolle. Ich denke, eine Schlüsselfrage ist, wie schnell wir mit so etwas wie "Bang Macros 1.1" weitermachen können.

Die PR wurde zusammengeführt, sodass Sie in der nächsten Nacht die folgenden Änderungen sehen können sollten. Die Funktion proc_macro_derive sollte das Element nicht mehr zurückgeben (dies löst einen Fehler über einen Typ aus, der zweimal definiert wird), und Sie können jetzt eine Liste von Attributnamen für die Whitelist wie diese #[proc_macro_derive(Derive, attributes(Foo, Bar)] bereitstellen.

cc @dtolnay , @sgrif ^

das wird leider bald brechen

Ja, ich habe https://github.com/serde-rs/serde/issues/614 eingereicht, um unser Ende zu verfolgen.

Ich glaube, ich habe den Dieselbruch in https://github.com/diesel-rs/diesel/pull/493 behoben und werde es mit Sicherheit wissen, sobald sich wieder Nachtschwärmer bauen.

Wenn ich diesen Thread also richtig lese, kann ein Proc-Makro, da es nur zusätzliche Elemente ausgeben kann, die an das ursprüngliche Element angehängt sind, auch keine weiteren Anmerkungen zum ursprünglichen Element hinzufügen? (Ich sehe eine Erwähnung, "Geschwisteranmerkungen" zuzulassen, aber sonst nichts.)

Ich habe ein #[derive(newtype)] proc-Makro in meiner (winzigen, unveröffentlichten) Kiste, das sich basierend auf der Struktur, die es kommentiert, auf eine andere Gruppe anderer #[derive()] . Zum Beispiel wird #[derive(newtype)] struct Foo(u64) auf #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] struct Foo(u64); während #[derive(newtype)] struct Foo(::semver::VersionReq) auf #[derive(Clone, Debug, PartialEq)] struct Foo(::semver::VersionReq); . Die Strukturelemente werden also nicht von diesem Makro geändert, sondern es werden andere Ableitungen hinzugefügt (diese ändern auch nicht die Struktur).

Es gibt ein paar Dutzend solcher Strukturen und jede hat ungefähr zehn neue Ableitungen, nachdem dieses Makro erweitert wurde. Ich mag es, wenn ich merke, dass alle u64 Newtypes ein weiteres Merkmal implementieren sollen, kann ich den Satz von Ableitungen einfach an einer Stelle im newtype Makrocode ändern, anstatt in jeder einzelnen Struktur.

Ich hatte dafür ein macro_rules newtype! -Makro, wechselte aber zu einem Proc-Makro, weil:

  • Das Vorhandensein oder Fehlen von Dokumentkommentaren, das Vorhandensein oder Fehlen von pub usw. wird kostenlos behandelt, ohne dass eine kombinatorische Anzahl von Makro-Match-Armen erforderlich ist.
  • Selbst wenn ich die kombinatorischen Makro-Match-Arme schrieb, war es schwierig, eine Reihenfolge zu finden, in der sie nicht miteinander in Konflikt standen.

Leider nein, Sie können dies nicht mehr so ​​tun, wie Sie es getan haben.

In Bezug auf die zukünftige Kompatibilität dieser Funktion ist sie stabil: Da die Plugin-Funktion nicht "rein" sein muss, ist dies eine wichtige Änderung, wenn sich die Reihenfolge der Objekte, die der verarbeiteten Funktion zugewiesen wurden, in Zukunft ändert oder wenn rustc Multithreading implementiert kompilieren?

@ est31 Wenn wir Zeit haben, sollten wir versuchen, die erwähnte IPC-Isolationssache durchzuziehen.

Nach den letzten Änderungen sehe ich immer wieder einen ICE in Diesel.

../src/librustc_metadata/decoder.rs:490: Eintrag: ID nicht gefunden: DefIndex (1) in Kiste "diesel_codegen" mit Nummer 28

@sgrif das wäre das Problem # 37788, das durch # 37793 behoben wird (hoffen wir, dass es morgen Abend endet ...).

@ est31 Es ist zu spät um diese Zeit, um es zusammenzuführen, bevor der nächtliche Build beginnt.

https://github.com/rust-lang/rust/issues/37839 ist ein Problem bei der Verwendung einer lib-Kiste, die selbst eine proc_macro-Kiste verwendet. AFAICT Keiner der Tests ist davon betroffen, da entweder das Proc-Makromodul oder ein Proc-Makromodul und ein Bin-Modul kompiliert werden, die direkt auf das Proc-Makro verweisen.

Edit: Jetzt behoben!

@Arnavion Das Problem, das Sie mit # 37839 gesehen haben, wurde wegen regelmäßiger Komplikationen behoben, bleibt jedoch bei Verwendung von --target defekt, wie in # 37958 berichtet. Ich habe einen minimalen Testfall mit --target bereitgestellt, der immer noch kaputt geht.

@rfcbot fcp zusammenführen

Nachdem die Attributfunktion für die Whitelist implementiert wurde , sollten wir dies meiner Meinung nach stabilisieren! Serde, Diesel und andere Benutzer - jetzt können Sie das Objekt ändern, wenn das aktuelle Design für Sie nicht funktioniert. =)

cc @sgrif @erickt

Teammitglied @nikomatsakis hat vorgeschlagen, dies zusammenzuführen. Der nächste Schritt ist die Überprüfung durch die übrigen markierten Teams:

  • [x] @alexcrichton
  • [x] @aturon
  • [x] @brson
  • [x] @eddyb
  • [x] @japaric
  • [x] @michaelwoerister
  • [x] @nikomatsakis
  • [x] @nrc
  • [x] @pnkfelix
  • [x] @vadimcn
  • [x] @ohne Boote
  • [x] @wycats

Derzeit sind keine Bedenken aufgeführt.

Sobald diese Prüfer einen Konsens erreicht haben, tritt dies in die endgültige Kommentierungsphase ein. Wenn Sie ein großes Problem entdecken, das zu keinem Zeitpunkt in diesem Prozess angesprochen wurde, melden Sie sich bitte!

In diesem Dokument finden Sie Informationen darüber, welche Befehle Teammitglieder mit Tags geben können.

Ich würde gerne sehen, wie in naher Zukunft Knallmakros erforscht werden. Keine Einwände.

@rfcbot überprüft

Derzeit wird ein leerer LexError zurückgegeben, wenn ein TokenStream nicht analysiert werden kann. Wäre es möglich, hier eine bessere Fehlermeldung zurückzugeben, z. B. welches Token konnte nicht analysiert werden? Obwohl Benutzer Ihrer Bibliothek diese Art von Fehlermeldungen wahrscheinlich nicht sehen möchten.

Ja, das wäre praktisch für Makroautoren. Ich musste den Stream in eine Datei schreiben und auf dem Spielplatz anzeigen, um Fehler zu finden.

Ich denke, Benutzer werden auch davon profitieren, wenn sie nur bessere Fehlerberichte gegen das Makro einreichen.

Drüben auf https://github.com/rust-lang/rust/pull/38140 erzwingen wir, dass benutzerdefinierte Ableitungserklärungen öffentlich sind. Die Theorie ist, dass wir eines Tages private Ableitungen wollen und dann wahrscheinlich diese Unterscheidung basierend auf der Sichtbarkeit der definierenden Funktion treffen wollen. In jedem Fall ist dies die konservative Wahl. Ich dachte, ich würde es ein oder zwei Tage lang marinieren lassen, damit die Leute es sehen können, bevor es zusammengeführt wird, da wir diese Funktion stabilisieren.

# 37480 ist geschlossen, was in der Checkliste berücksichtigt werden sollte.

Fest

@pnkfelix Ich erlaubt , Ihr Kästchen für Sie zu überprüfen, da Sie bei PTO sind und ich glaube, dass Sie voll an Bord sind. Bitte lassen Sie uns wissen, wenn dies nicht der Fall ist!

: bell: Dies tritt nun in die endgültige Kommentierungsphase ein , wie oben beschrieben . :Glocke:

psst @nikomatsakis , ich konnte das final-comment-period -Label nicht hinzufügen, bitte tun Sie dies.

Geht die bevorstehende Stabilisierung davon aus, dass die verbleibenden bekannten Fehler in der Checkliste oben zuerst behoben werden? Oder blockieren diese nicht zur Stabilisierung?

Im Moment gibt es zwei:

  • # 36935 hat einen Kommentar, der besagt, dass es gelöst ist.
  • # 36691 scheint mir nicht zu blockieren, wir können zulassen, dass diese eines Tages auf mod foo; werden, wenn wir wollen, ohne etwas zu brechen, was ich glaube.

Hallo! Dies ist ein RFC 1636- Dokumentationsaudit. Dies ist das erste wichtige Merkmal, das sich seit der Annahme von RFC 1636 stabilisiert hat, und wir sollten es in diesem Fall gut befolgen.

Der RFC besagt:

Vor dem Stabilisieren eines Features werden die Features nun wie folgt dokumentiert:

  • Sprachmerkmale:

    • muss in der Rostreferenz dokumentiert werden.

    • sollte in der Programmiersprache The Rust dokumentiert werden.

    • kann in Rust anhand eines Beispiels dokumentiert werden.

  • Sowohl Sprachfunktionen als auch Standardbibliotheksänderungen müssen Folgendes umfassen:

    • eine einzelne Zeile für das Changelog

    • eine längere Zusammenfassung für die Ankündigung der Veröffentlichung in Langform.

Was ist der richtige Prozess dafür? Sollten wir dem obersten Kommentar dieser Ausgabe Checklistenelemente hinzufügen oder eine neue Ausgabe für die Nachverfolgungsdokumentation erstellen? Es scheint mir, dass wir bis zur Version 1.15 eine Dokumentation haben sollten, die diese Anforderungen im Baum erfüllt.

cc @ rust-lang / docs

Danke @withoutboats ! Es ist der erste große, ja. Ich hatte das heute Morgen auf meiner Liste, um es mir anzusehen, und siehe da, du hast mich geschlagen 😄

Ich stellte mir immer vor, dass die Entscheidung zur Stabilisierung und die tatsächliche Stabilisierung getrennt sind. Das heißt, @ rust-lang / lang kann sagen, dass "dies stabil gemacht werden kann", aber das Commit zum Entfernen des Gates stellt auch sicher, dass die Dokumentation vorhanden ist. In einer Welt, in der das instabile Buch existiert , würde dies die Dokumente von dort in die stabilen Dokumente ziehen. aber bis dahin sind die Dinge etwas umständlich.

Angesichts der Tatsache, dass wir gerade eine Veröffentlichung hatten, war mein Plan, so etwas zu tun:

  1. Warten Sie, bis FCP verlassen ist
  2. Lande einige Dokumente. (Ich hatte vor, sie in diesem Fall zu schreiben)
  3. Machen Sie die Stabilisierung PR.

Möglicherweise zwei und drei kombinieren.

/ cc @ rust-lang / core, da dies ein teamübergreifendes Problem ist.

@steveklabnik das klingt gut für mich und ich wäre in

Es wäre auch gut, diese schnell zu bekommen, damit wir einen sicheren Backport zum 1.15 Beta-Zweig gewährleisten können!

Wenn ich der erste bin, der dies trifft, kann es nicht so schlimm sein, aber lassen Sie es uns unter bekannten Fehlern einschließen: Die Verwendung eines Typmakros in einer Struktur mit einer benutzerdefinierten Ableitung verursacht einen ICE https://github.com/rust-lang/ Rost / Probleme / 38706

https://github.com/rust-lang/rust/pull/38737 behebt die Typmakros ICE: heart:. Gibt es eine Chance, dies in die Beta zurückportieren zu lassen? Begründung: Es scheint schlecht, dass zwei herausragende neue Funktionen, eine in 1.13 und eine in 1.15, den Compiler zum Absturz bringen, wenn Sie sie zusammen verwenden.

Ich habe gerade # 38749 bezüglich der Dokumentation der proc_macro Kiste erstellt.

Ich habe mehrmals gelesen, dass Macros 1.1 in 1.15 stabilisiert wird, aber 1.15.0-beta.1 wurde vor zwei Wochen ausgeliefert und mindestens extern crate proc_macro; ist immer noch in diesem sowie im nächtlichen 4ecc85beb 2016 enthalten -12-28. Ist der Plan, die Stabilisierungsänderung rückgängig zu machen?

@ SimonSapin Ja, das war der Plan, aber wir müssen es schaffen!

Es ist immer noch der Plan: p

Wenn der Benutzer #[derive(Foo)] #[foo_def = "definition.json"] struct MyStruct; schreibt, kann der Makro-Handler nicht wissen, was "das aktuelle Verzeichnis" ist, und kann daher definition.json nicht finden.

Dies ist beabsichtigt und daher nicht einfach zu beheben, und ich denke, es ist sowieso zu spät, dies zu beheben.

Sie können das Verzeichnis Span -> FileMap -> Dateiname -> auswählen. Es fehlt lediglich der Zugriff auf die Informationen über proc_macro .

Sie müssten den Compiler auch anweisen, definition.json eine Abhängigkeit hinzuzufügen, damit der Build verschmutzt ist, wenn er geändert wird.

Das proc-Makro kann env::var("CARGO_MANIFEST_DIR") , um das Verzeichnis mit Cargo.toml der Kiste mit dem Makroaufruf abzurufen. Vermutlich ist foo_def relativ dazu. Siehe https://github.com/dtolnay/syn/issues/70#issuecomment -268895281.

@tomaka , das durch Mutieren von FileMap ausgeführt werden kann, z. B. so funktioniert das Makro include_str! .

Vermutlich ist foo_def relativ dazu.

Ich denke, es ist nicht sehr intuitiv, den Pfad relativ zur Cargo.toml zu setzen.

Dies kann durch Mutieren von FileMap erfolgen, z. B. so wird include_str! Makro macht es.

Ja, ich weiß, dass dies möglich ist. Mit der aktuellen API für prozedurale Makros ist dies einfach nicht möglich.

Der Parameter ist ein Item . Es wäre akzeptabel, eine Methode hinzuzufügen, um eine Spanne von diesem Element abzurufen, aber das Hinzufügen einer Methode zu Item , um den Compiler zu bitten, eine Abhängigkeit hinzuzufügen, wäre eine Hack-IMO.

Sie können Span -> FileMap -> Dateiname -> Verzeichnis gehen.

Befinden sich diese APIs (insbesondere FileMap ) auf einem zu stabilisierenden Pfad?

Sie müssen es nicht sein, tatsächlich würde ich keines der Interna stabilisieren wollen. Wir können stattdessen APIs stabilisieren, die Informationen über ein Span extrahieren (z. B. Zeile, Spalte, Dateiname).

Ich habe gerade diesen Fehler in einer Kiste von mir bekommen. Was ist los?

`` error: Cannot use #! [Feature (proc_macro)] and #! [Feature (custom_attribute)] zur gleichen Zeit
`` ``

@alexreg Wenn Sie #[derive] , ist es jetzt stabil. Sie brauchen nicht #![feature(proc_macro)] .

@alexreg
proc_macro_derive s (Makros 1.1) sind jetzt stabil - Sie können einfach #![feature(proc_macro)] entfernen.

#[proc_macro_attribute] kürzlich hinter dem Feature-Gate #![feature(proc_macro)] gelandet. Diese sind nicht kompatibel mit #![feature(custom_attribute)] . #![feature(custom_attribute)] werden veraltet, sobald der Ersatz landet (https://github.com/rust-lang/rfcs/pull/1755).

@jseyfried Ich denke, wir sollten das Tracking-Problem für proc_macro ändern, da dies geschlossen ist und keine relevanten Informationen enthält.

Danke Leute. Das macht Sinn.

@abonander Ja, #![feature(proc_macro)] sollte jetzt definitiv auf # 38356 verweisen - das hätte ich bei der Überprüfung von # 38842 überprüfen müssen. Könnten Sie eine PR einreichen?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen