Go: embed, cmd/go: Unterstützung für eingebettete Dateien hinzufügen

Erstellt am 2. Sept. 2020  ·  114Kommentare  ·  Quelle: golang/go

Im Juli haben @bradfitz und ich einen Entwurf für eingebettete Dateien veröffentlicht . Das Dokument verlinkt auf ein Video, einen Prototypcode und eine Reddit-Diskussion.

Das Feedback zu diesem Design war überwältigend positiv.

Ich schlage vor, den Entwurf für eingebettete Dateien für Go 1.16 zu übernehmen, mit einer in der Diskussion vorgeschlagenen Ergänzung, um den Fall des direkten Zugriffs auf die Bytes in einer einzelnen eingebetteten Datei zu vereinfachen.

Solange eine Datei "embed" importiert ( import _ "embed" falls erforderlich), ist es erlaubt //go:embed , um eine einzelne Datei zu benennen (keine Globmuster oder Verzeichnisabgleiche erlaubt) zu initialisieren eine einfache string oder []byte Variable:

//go:embed gopher.png
var gopherPNG []byte

Der Import ist erforderlich, um die Datei so zu kennzeichnen, dass sie //go:embed Zeilen enthält und verarbeitet werden muss. Goimports (und gopls usw.) kann diese Regel beigebracht werden und der Import nach Bedarf automatisch in jede Datei mit einem //go:embed eingefügt werden.

Das Design der eingebetteten Dateien hängt vom Entwurf der Dateisystemschnittstelle ab, den ich auch in #41190 vorgeschlagen habe.

Bei diesem Thema geht es _nur_ um die Übernahme des Designs für eingebettete Dateien , unter der Annahme, dass auch das Design der Dateisystemschnittstelle übernommen wird. Wenn dieser Vorschlag akzeptiert wird, bevor das Design der Dateisystemschnittstelle fertig ist, würden wir einfach auf das Design der Dateisystemschnittstelle warten, bevor wir mit den Änderungen beginnen.

Proposal Proposal-Accepted

Hilfreichster Kommentar

Keine Änderung im Konsens, also akzeptiert.

Alle 114 Kommentare

Wäre es ein Fehler, eine //go:embed Direktive zu haben, ohne embed zu importieren?

@jimmyfrasche Ja, fünftletzter Aufzählungspunkt in der Liste unter https://go.googlesource.com/proposal/+/master/design/draft-embed.md#go_embed -directives.

@rsc Vielleicht habe ich es im Entwurf übersehen, aber ich sehe nicht die Möglichkeit, eine einzelne Datei einzubetten, die Sie in Ihrem Kommentar erwähnen.
Könnten Sie auch eine einzelne Datei als const-String einbetten?
Danke für diesen tollen Vorschlag.

@pierrec Es ist nicht im Entwurfsdokument enthalten (die "eine Ergänzung" ist der Text im obigen Kommentar). Const-Strings können letztendlich eine Rolle bei der Entscheidung spielen, ob ein Programm den Typ prüft, was bedeuten würde, dass alle Typprüfer //go:embed'ed consts verstehen müssen. Im Gegensatz dazu, wenn wir uns an vars halten, sind Typprüfer nicht klüger und können in Ruhe gelassen werden. Scheint, als sollten wir wahrscheinlich bei Vars bleiben.

Gibt es einen bestimmten Grund, warum Sie eine const anstelle einer var wollten? Ihre Verwendung sollte in Bezug auf die Effizienz ungefähr gleich sein. (Referenzen auf const-Strings kompilieren sowieso so viel wie Referenzen auf versteckte Variablen.)

Danke für die Erklärung. Ich neige derzeit dazu, statische Assets als konstante Zeichenfolgen einzubetten, deshalb habe ich nachgefragt. Mir geht es auch gut mit Vars!

Interessant, also könnte ich sowas machen:

//go:embed version.txt
var Version string

Und möglicherweise sogar einen //go:generate Kommentar haben, um version.txt zu generieren. Das würde einen großen Anwendungsfall für Makefiles/ldflags ausschließen.

Ist es ein Fehler, wenn die Datei nicht gefunden wird? Wenn ja, wo wird der Fehler technisch betrachtet? Linkzeit?

Können wir sicherstellen, dass go:embed nach go:generate ausgeführt wird, damit wir Dinge wie das einfache Generieren von Versionen usw. tun können?

Können wir sicherstellen, dass go:embed nach go:generate ausgeführt wird, damit wir Dinge wie das einfache Generieren von Versionen usw. tun können?

Nach meinem Verständnis wird go:generate mit go generate während go:embed mit go build passieren würde.

@carlmjohnson Ja, es ist immer ein Fehler, //go:embed foo zu sagen, wo foo nicht existiert.
Der Fehler tritt beim Kompilieren der Quelldatei auf, die diese Zeile enthält.
(Wenn Sie foo nach dem Kompilieren dieser Quelldatei entfernen würden, würden Sie immer noch nicht zu einem Link-Schritt gelangen - der Befehl go würde feststellen, dass das Paket neu erstellt werden muss, weil foo gelöscht wurde.)

Ich denke, dass dieser Vorschlag nicht vollständig ist, ohne etwas über ETag zu sagen.
https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fzi0pok/

@tv42 , ja, wir werden ETag zum
(Auch bestätigt auf https://github.com/golang/go/issues/35950#issuecomment-685845173.)

Zwei Drei Dinge, die mir bei der Arbeit mit mjibson/esc aufgefallen sind:

  • Da go:embed keine go-Dateien für die Einbettung als schreibgeschütztes Dateisystem generieren muss, würde es den Aufwand des Änderns von Zeitstempeln für die go:generate ed-Dateien, die git porcelain defi widersetzten, beseitigen Tests auf CI- sehr schön
  • Eine Sache, die ich im Vorschlag nicht gefunden habe, aber brauche, ist die Möglichkeit, die eingebetteten Dateien während der Entwicklungszyklen live neu zu laden. Mit mjibson/esc kann ich das derzeit tun, indem ich es anweist, das lokale Dateisystem zu verwenden (obwohl es keine neuen Dateien aufnehmen würde) und das Verhalten mithilfe von Build-Tags zu ändern. Ich frage mich, was das in den Vorschlag passen könnte?
  • Update Eine andere Sache, an die ich mich erinnere, ist, dass esc erforderlich ist, um den Basispfad (Teile davon) transparent zu entfernen, um zB einen Asset-Ordner als Web-Root zu exportieren.

Nachtrag : Ich denke, der zweite Punkt könnte in Verbindung mit dem io/fs Vorschlag behoben werden, bei dem ich entweder das eingebettete oder das Live-Dateisystem für die Aufnahme verwenden würde? Implementieren Sie das Entfernen von Pfaden als io/fs Middleware?

@andig Sie können Präfixe bereits entfernen, wenn Sie ein Dateisystem über HTTP bereitstellen . Ich stimme zu, dass das Live-Nachladen von einer Drittanbieterbibliothek durchgeführt werden kann, die ein io/fs .

Noch eine Sache: Wenn ich das richtig verstehe, berücksichtigt embed Dateien lokal im Paket und verbietet .. . Mein aktuelles Design hat /assets und /server/ wobei letzteres den Code des Servers enthält und heute die generierten Dateien hostet. Bei diesem Vorschlag müsste die Einbettung in den Stammordner verschoben werden, da auf die Assets vom Server aus nicht zugegriffen werden kann. Dies führt zu anderen Einschränkungen der Zugänglichkeit als bei normalen Importen. Ich habe mich gefragt, ob dies aus Sicherheitsgründen notwendig ist oder ob modullokale Einbettungen generell erlaubt sein sollten.

Noch eine Sache: Wenn ich das richtig verstehe, berücksichtigt embed Dateien lokal im Paket und verbietet .. . Mein aktuelles Design hat /assets und /server/ wobei letzteres den Code des Servers enthält und heute die generierten Dateien hostet. Bei diesem Vorschlag müsste die Einbettung in den Stammordner verschoben werden, da auf die Assets vom Server aus nicht zugegriffen werden kann. Dies führt zu anderen Einschränkungen der Zugänglichkeit als bei normalen Importen. Ich habe mich gefragt, ob dies aus Sicherheitsgründen notwendig ist oder ob modullokale Einbettungen generell erlaubt sein sollten.

Sie können eine emed.go-Datei in Ihrem Asset-Verzeichnis erstellen und die Assets als eigenes Paket für den Rest Ihres Programms verfügbar machen.

Ein weiteres explizites Ziel ist es, einen Sprachwechsel zu vermeiden. Für uns scheint das Einbetten statischer Assets ein Werkzeugproblem zu sein, kein Sprachproblem.

Einverstanden. Meiner Meinung nach ist das Hinzufügen von syntaktischem Zucker in der Sprache, um diesen Werkzeugwechsel zu unterstützen, eine Sprachänderung. Ich bin mir sicher, dass dies für andere offensichtlich ist, aber dies ist effektiv ein Kommentar als Code.

Ich habe das starke Gefühl, dass Magie/Zucker die Einfachheit und Lesbarkeit der Sprache beeinträchtigen; Es ist sehr leicht, einen magischen Kommentar zu übersehen, der eine Datei einbettet. Während eine Antwort darauf leicht "okay, dann nicht verwenden" lauten könnte, bedeutet diese Änderung, dass ein Prüfer weiterhin wachsam sein muss, wenn andere diese Funktion verwenden, und sich daran erinnern muss, dass Kommentare zu Variablendeklarationen Builds unterbrechen oder fehlschlagen können Kompilierungszeit.

Ich glaube, dies wird zu Verwirrung führen, die Sprachverwendbarkeit beeinträchtigen und zu undurchsichtigen, großen Binärdateien ohne klaren Nutzen führen (in Bezug auf die letztere Besorgnis wird dies sogar zu einem Anti-Muster der Neubildung von Binärdateien aufgrund von einfachen Dateiänderungen führen ). Wenn go mod ein --withNonGoCodeAssets go mod zulassen würde, würde dies meiner Meinung nach die Anforderungen der meisten Entwickler erfüllen, die keine komplexeren Build-Pipelines schreiben möchten (ich gehe davon aus, dass die Endbenutzerverteilung eine kleinere Teilmenge von das Problem für Benutzer).

@tristanfisher , ich verstehe Ihren Standpunkt zu Sprache vs. Werkzeugwechsel. Es ist sicherlich in der Nähe der Linie. Der Grund, warum ich es eher als Werkzeugänderung betrachte, ist, dass die Sprachspezifikation nicht betroffen ist - ob ein Programm gültig ist, ändert sich nicht, der Typprüfungsprozess ändert sich nicht. Es ändert sich lediglich der Anfangswert dieser Variablen nach dem Kommentar. Auf diese Weise ähnelt es dem Flag -X des Linkers, das den Anfangswert einer Variablen der obersten Ebene vom Typ string setzen kann. Es ist in Ordnung, wenn wir anderer Meinung sind; Ich wollte nur meine Definition klarstellen und die Unterscheidung erklären, die ich mache.

Was Blähungen angeht, müssen wir wohl sehen, aber ich rechne nicht damit, dass die Programme noch viel größer werden, als sie es ohnehin schon sind. Die Leute führen _bereits_ Tools aus, die beliebige Dateien in Go-Code umwandeln, checken sie in ihre Repos ein und lassen sie vom Compiler erstellen. Das Design entfernt etwas Overhead aus diesem Prozess, ermöglicht jedoch nichts Neues. Vielleicht missbrauchen die Leute es jetzt, da es einfacher zu machen ist, aber insgesamt erwarte ich nicht, dass dies ein großes Problem darstellt. (Und wenn eine Abhängigkeit etwas so Großes einbettet, dass es Ihre Binärdateien aufbläht, können Sie sich jederzeit dafür entscheiden, diese Abhängigkeit nicht zu verwenden.)

Bei Neuaufbauten aufgrund von einfachen Dateiänderungen sind die einzigen Dateien, die Neuaufbauten auslösen können, diejenigen in Ihrem eigenen Modul der obersten Ebene, da Abhängigkeiten unveränderlich sind. Wenn Sie feststellen, dass Neuerstellungen häufiger als gewünscht auftreten, besteht die einzige Erklärung darin, dass Sie (1) Dateien einbetten und (2) diese Dateien ändern. Sie hätten die vollständige Kontrolle darüber, ob Sie etwas gegen beide Ursachen unternehmen könnten. (Es wäre eine ganz andere Sache, wenn die Wahl einer Abhängigkeit, was sie verwenden soll, Sie irgendwie zu zusätzlichen Neuaufbauten oder anderen Ausgaben zwingen würde. Aber das ist hier nicht der Fall.)

@rsc Ich stimme zu, dass es in Ordnung ist, freue mich über Ihre Antwort. Mein Gefühl ist, dass es sich um eine Sprachänderung handelt, wenn es standardmäßig in den Standardtools enthalten ist und Kommentare zu einer impliziten Initialisierung einer Variablen führen können. Außerhalb dieser Debatte denke ich, mein ekliges Gefühl dreht sich um mehr Anweisungen als "magische" Kommentare, die von (menschlichen) Codelesern auswendig gelernt werden müssen. Dies könnte zu dem absurden Schluss geführt werden, neue Funktionen über Blockkommentare hinzuzufügen, die zur Build-Zeit behandelt werden.

Wenn dies jedoch zum Ökosystem hinzugefügt wird, bin ich dankbar, dass das Importieren von embed erforderlich ist – das ist besser als nichts als "Hey, Kopf hoch" beim Auditieren von Code. Ich denke, go mod würde die meisten Anwendungsfälle lösen (ich stelle mir vor, die meisten Leute werden Dateien für Webserver globen) und würde auch vollständig in Tools leben.

Ich denke, Ihr Punkt bezüglich des Linkers ist gut. Es hilft auch, meine Meinung dazu zu erklären: Wenn der Endbenutzer (zB nicht jemand, der einfach ein Paket importiert) die Entscheidung trifft, gibt es keine Möglichkeit, von Klumpen von Nicht-Code überrascht zu werden, die mitkommen. Meine Bedenken entstehen aus der Überprüfung / Abstimmung mit der Arbeit anderer und aus "technologischer" Verantwortung, weshalb ich das Bedürfnis verspürte, darauf zu reagieren.

Ich denke, "wir müssen sehen" fasst es gut zusammen (ich bin eher zynisch, was Aufblähen / Missbrauch angeht).

Ich werde heute Abend den Entwurf durchlesen, soweit sieht er aus der TinyGo-Perspektive gut aus.

Ich wollte nur eines klarstellen:

Auf der anderen Seite sind Projekte wie TinyGo und U-root Zielsysteme mit mehr RAM als Disk oder Flash. Bei diesen Projekten könnte die Komprimierung von Assets und die Verwendung inkrementeller Dekomprimierung zur Laufzeit erhebliche Einsparungen bringen.

Ich kenne U-Root nicht, aber für TinyGo sind die Hauptziele Mikrocontroller, die normalerweise viel mehr Flash als RAM haben (normalerweise einen Faktor von 8 oder 16). Ein kurzer Blick auf den Entwurfsentwurf scheint darauf hinzudeuten, dass die Dateien im Nur-Lese-Speicher gehalten werden sollen, was für diese Ziele gut funktionieren würde: Die eingebetteten Dateien können direkt aus dem Flash gelesen werden. Für TinyGo-Ziele wäre es höchstwahrscheinlich nicht wünschenswert, Dateien zur Laufzeit zu dekomprimieren.

Der io/fs-Vorschlag, von dem dies abhängt, scheint bei Readdir/FileInfo-Problemen blockiert zu sein, die in #41188 und zuvor #40352 diskutiert werden.

Ich habe eine API entworfen, um sie in https://github.com/golang/go/issues/41188#issuecomment -686283661 zu ersetzen

@andig

Eine Sache, die ich im Vorschlag nicht gefunden habe, aber brauche, ist die Möglichkeit, die eingebetteten Dateien während der Entwicklungszyklen live neu zu laden.

embed.Files implementiert fs.FS, daher müssen Sie nur dev vs !dev build tag verwenden, um eine Variable zwischen embed.Files und dem echten FS umzuschalten.

Ich habe #41265 eingereicht. Es bietet eine neue ReadDir()-API für io/fs.

Ich habe ähnliche Bedenken wie @tristanfisher . Go verwendet seit langem (von Anfang an?) magische Kommentare als Compiler-Direktiven, aber sie sind für Eckfälle gedacht und kommen selten im Code vor. Angesichts der Popularität des Einbettens statischer Inhalte in Go-Binärdateien wird //go:embed wahrscheinlich häufiger vorkommen. Vielleicht ist es an der Zeit, eine andere Syntax für Compiler-Direktiven in Betracht zu ziehen?

Nur zur Erinnerung, dass das Ändern der Go-Syntax sehr hohe Kosten verursacht. So ziemlich jedes Go-Tool auf dem Markt müsste aktualisiert und/oder repariert werden, um beispielsweise die neue Syntax zu unterstützen.

Ich halte sie nicht für magische Kommentare. Zeilen, die mit //go: sind Direktiven und könnten als solche in der Spezifikation definiert werden. Es gibt keinen großen semantischen Unterschied zwischen //go:embed , @embed , [[embed]] oder einer anderen Anzahl von Syntaxvarianten, außer dass das Präfix //go: bereits als nicht behandelt wird -Code von Go-Tools. (mein Redakteur hebt diese Zeilen zum Beispiel anders hervor)

@mvdan Wenn dieser Vorschlag

@ianund ich bin nicht

Ich halte diesen Vorschlag für eine gute Idee. Es löst ein häufiges Problem. Meine Sorge ist, dass die Kosten für die Annahme etwas deutlicher gemacht werden sollten.

@jonbodner Ich teile deine Bedenken bezüglich magischer Kommentare. Aber bis zu einem gewissen Grad werden die Regeln durch #37974 spezifiziert.

@networkimprov , dies ist nicht der io/fs-Vorschlag. Bitte hören Sie hier auf, über ReadDir zu kommentieren.

@jonbodner

Ich bin nicht pingelig, was die spezifische Syntax für Compiler-Direktiven angeht. Ich denke nur, dass es irgendwann formalisiert und die Regeln festgelegt werden müssen.

Ich möchte nur darauf hinweisen, dass wir die Entscheidung getroffen haben, //go: zu verwenden, um Go-Toolchain-Direktiven zu markieren, wenn
Wir haben (die eingeschränkte Verwendung) //go:nointerface Anmerkung bereits 2012
2013 haben wir //go:noescape für Assembly-Autoren
Wir haben 2014 //go:generate hinzugefügt .
Wir werden wahrscheinlich auch 2020-2021 //go:build hinzufügen .
Da sind andere; das sind nur die Highlights.
Sie können sich //go: als #pragma aus C vorstellen, wenn es hilft.

An dieser Stelle ist die Konvention sehr gut etabliert.
Wir haben diese Syntax bereits 2012 gewählt, weil
(1) es ist offensichtlich kein Kommentar für eine Person;
(2) Tools, die die Kommentare nicht kennen, ignorieren sie, da es sich um Kommentare handelt; und
(3) es generalisiert auf andere Tools (s/go/yourtool/).

Und wie Ian sagte, hat #37974 die exakte verallgemeinerte Kommentarsyntax formalisiert, was das wert ist.

Basierend auf der obigen Diskussion scheint dies eine wahrscheinliche Akzeptanz zu sein .
(Wieder unter der Annahme, aber getrennt vom FS-Vorschlag.)

Keine Änderung im Konsens, also akzeptiert.

Ich bin gespannt darauf, Embed in die Hände zu bekommen - kann dies bereits auf dem Master getestet werden oder ist es geplant, es während des 1.15-Zyklus als Experiment auszuliefern?

@andig , Go 1.15 ist schon raus. Ich hoffe immer noch, dass dies in Go 1.16 sein wird und diesen Monat im Entwicklungszweig landet.

@rsc 1.16 verfügbar?

@septs , nein, wir arbeiten noch an Go 1.16. Der Code-Freeze ist der 31. Oktober mit einem angestrebten Veröffentlichungsdatum vom 1. Februar.

schnellste Veröffentlichung 2021Q1 oder 2021Q2?

@septs bitte hör auf, in diesem Thread Fragen zu Go-Releases zu stellen. Über zwanzig Leute folgen ihm und werden benachrichtigt. Siehe https://golang.org/wiki/Questions und https://github.com/golang/go/wiki/Go-Release-Cycle.

Änderung https://golang.org/cl/243941 erwähnt dieses Problem: go/build: recognize and report //go:embed lines

Änderung https://golang.org/cl/243940 erwähnt dieses Problem: go/build: refactor per-file info & reader

Änderung https://golang.org/cl/243942 erwähnt dieses Problem: embed: implement Files

Änderung https://golang.org/cl/243944 erwähnt dieses Problem: cmd/compile: add //go:embed support

Änderung https://golang.org/cl/243945 erwähnt dieses Problem: cmd/go: add //go:embed support

Ein Detail, das in der Implementierungsüberprüfung auftauchte, ist, dass "Files" als Nomen im Singular ziemlich umständlich ist ("A Files hält ...").

Die Wahl von embed.Files für den Namen lag sowohl vor dem io/fs-Vorschlag als auch vor der Unterstützung von String und []Byte.
Angesichts dieser beiden Entwicklungen besteht eine scheinbar vernünftige Möglichkeit, das Problem "A Files hold" zu lösen, darin, es als FS anstelle von Files zu bezeichnen.

Dann sind die drei Möglichkeiten zum Einbetten und Drucken von Daten:

import "embed"

//go:embed hello.txt
var s string
print(s)

//go:embed hello.txt
var b []byte
print(string(b))

//go:embed hello.txt
var f embed.FS
data, _ := f.ReadFile("hello.txt")
print(string(data))

Das scheint klarer zu sein, was Sie erhalten: eine Zeichenfolge, ein []Byte oder ein FS.
Das heißt, der größte Teil der Funktionalität von embed.F* kommt daher, dass es ein fs.FS ist, und wenn man es FS nennt, wird dies klarer als es Files zu nennen.

Ich habe diese Änderung in meinem neuesten Entwurf des CL-Implementierungspakets embed vorgenommen, aber ich wollte hier zurückkommen und sehen, ob es Einwände gegen die Namensänderung gibt.

(Eine radikalere Änderung wäre, var f fs.FS anstelle von var f embed.FS auszuführen, aber das würde ausschließen, dass jemals eine andere Methode als Open auf f . Zum Beispiel oben mit ReadFile ist praktisch und wäre nicht möglich. Und im Allgemeinen haben wir gelernt, dass die Verwendung eines konkreten Typs für etwas, das möglicherweise später Methoden hinzufügen möchte, im Vergleich zur direkten Verwendung eines Schnittstellentyps zukunftssicher ist.)

Ich finde die Umbenennung eine gute Abwechslung.

Zur radikaleren Änderung:

  • Wenn wir fs.FS würden, würden wir dann überhaupt noch das Paket embed brauchen? Ich denke, der dynamische Wert muss immer noch einen Typ haben, der in einem Paket lebt? Ich finde die Idee, kein Paket hinzufügen zu müssen, ein Pluspunkt.
  • Ich finde nicht , „wir Methoden nicht hinzufügen können“ Super zu überzeugen, weil IMO f.ReadFile(…) nicht signifikant weniger bequemer als fs.ReadFile(f, …) .
  • Ich stimme zu, dass konkrete Typen im Allgemeinen besser sind, also ist das ein Pluspunkt, um es beizubehalten embed.FS
  • Eine andere Frage: Verwendet embed.FS Zeigerempfänger oder Wertempfänger? IMO muss &f herumgeben, ist umständlich, die Verwendung von Wertempfängern ist etwas unerwartet. Wir könnten aber auch var f *embed.FS zulassen. Wenn die Variable einen Schnittstellentyp hat, verschwindet diese Frage.

Insgesamt stimme ich immer noch zu, dass die Verwendung des konkreten embed.FS besser ist - wenn nichts anderes, dann zu Dokumentationszwecken.

Jetzt, wo Sie es erwähnt haben, ist mir das nicht klar: Wir können Verzeichnisse einbetten, oder?

Ja als embed.FS, das fs.FS implementiert.

@Merovius , embed.FS verwendet Wertempfänger. embed.FS ist ein Ein-Wort-Struct, das einen einzelnen Zeiger enthält, also gibt es keinen wirklichen Aufwand dafür, aber es bedeutet, dass Sie sie überall zuweisen und verwenden können, ohne sich um *s und \&s kümmern zu müssen.

@chabad360 , ja, Sie können Verzeichnisse einbetten.

Was ist mit Symlinks?

@burik666 , siehe https://golang.org/s/draft-embed-design für Details, aber nein, Sie können keinen Symlink einbetten.

Wird es möglich sein, dynamische C-Bibliotheken einzubetten und zu verwenden? Wenn ja, wie würden wir den Einbettungspfad in #cgo Headern verwenden, wie zum Beispiel: #cgo LDFLAGS: -L./lib -lmylib -Wl,-rpath=./lib ?

@benitogf Ich dlopen . Ich kann mir nicht vorstellen, wie Sie dem dynamischen Loader sagen können, wie er eingebettete Dateien findet. Auch wenn Sie in C-Code bündeln möchten, scheint statische Verknüpfung sowieso angemessener zu sein, nicht wahr?

Mit @benitogf Einbetten können Sie eine Datei von der Festplatte bequem in ein []Byte in Ihrem Programm einfügen, mehr nicht.
Wenn Sie eine Möglichkeit haben, eine dynamische C-Bibliothek zu verwenden, die bereits in Ihrem Programm in Form eines []Bytes vorhanden ist, dann hilft Ihnen das Einbetten, dort eine Festplattendatei zu erhalten. Ansonsten nein.

statische Verknüpfung wäre sowieso angebrachter, nicht wahr?

@Merovius stimmte zu, aber ich habe mehrere Anwendungsfälle, die mit Anbietern arbeiten, die nur dynamische Bibliotheken bereitstellen

Wenn Sie eine Möglichkeit haben, eine dynamische C-Bibliothek zu verwenden, die bereits in Ihrem Programm in Form eines []Bytes vorhanden ist
die einzige wirkliche Möglichkeit, dies zu tun, wäre, sie auf die Festplatte zu schreiben und dlopen zu verwenden

Das Schreiben der eingebetteten Bibliothek aus dem []Byte in das Dateisystem und die Verwendung von dlopen scheint in Ordnung zu sein, obwohl es nützlich wäre, die eingebetteten Dateien beim Erstellen/Ausführen optional in das Dateisystem "abzuladen", damit der #cgo Header darauf zugreifen kann, nicht nur für cgo imho

Probieren Sie es jetzt aus; Eine Warze mit der go:embed Direktive ist, dass, wenn ich build/* einbette, die Dateinamen immer noch das Präfix build/ . Wenn ich dieses Verzeichnis dann über http.FS bedienen möchte, gibt es keine einfache Möglichkeit, das Präfix, das erforderlich ist, um bei Bedarf darauf zuzugreifen, _hinzufügen_ (ohne einen Wrapper zu schreiben, was dann auf das Problem stößt, jede potenzielle Methode auflisten zu müssen) die die FS haben kann...).

z.B:

//go:embed build/*
var buildDir embed.FS

// Serve some SPA build dir as the app; oops, needs to be build/index.html
http.Handle("/", http.FileServer(http.FS(buildDir)))

// or

//go:embed static/*
var staticDir embed.FS

// Oops; needs to have a static prefix.
http.Handle("/static/*, http.StripPrefix("/static", http.FileServer(http.FS(staticDir))))

// Could be this, but only because the prefix happens to match:
http.Handle("/static/*, http.FileServer(http.FS(staticDir)))

Ich weiß, die Absicht ist, dass man go:embed foo/* bar/* baz.ext schreiben und all diese Dateien abrufen könnte, aber ich denke, es wird sehr üblich sein, einfach ein Verzeichnis einzubetten und es als statisches Asset über das http-Paket bereitzustellen. Ich gehe davon aus, dass dies ein Fallstrick ist, da die Leute von Dingen wie http.Dir("static") oder pkger.Dir("/internal/web/static") denen das Präfix bereits behandelt wird, zu den neuen embed.FS wechseln.

Ich bin mir nicht wirklich sicher, wie ich das einreichen soll, da es eine Art Zusammenspiel mit embed , io/fs und net/http .

@zikaeroh Das Schreiben eines http.Handler Wrappers würde dort auch funktionieren, oder? Das ist immer nur eine Methode und es gibt sogar http.HandlerFunc . Vielleicht könnte die Standardbibliothek sogar eine zum Spiegeln von http.StripPrefix bereitstellen (etwa http.AddPrefix oder http.ReplacePrefix ).

Obwohl es sich möglicherweise etwas seltsam anfühlt, die HTTP-Anforderung zu ändern, um die FS-Implementierung zu umgehen (im Gegensatz zu einem verallgemeinerten "Gib mir ein FS, das ein Unterverzeichnis eines anderen FS ist", was mit optionalen Methoden nicht einfach ist). Es wäre nicht die effizienteste Sache, zu entfernen und dann wieder ein weiteres Präfix hinzuzufügen (bei http.Request Kopien), aber ich werde es später versuchen. Es ist zumindest nicht _anders_ als das derzeitige Schema der Dinge, wo Sie mit der Anfrage arbeiten müssen, nehme ich an.

Ich habe ein paar andere Orte, an denen ich statische Daten verwende, nicht über das http-Paket, für die ich eine ähnliche Lösung finden muss.

Wenn ich sehen muss, wie es umgesetzt wird, wo kann ich suchen. Eine Filiale, in der es umgesetzt wird?

Es wurde vorher vorgeschlagen, die Dateien direkt einzubetten, dh in das Build-Verzeichnis zu tun und es dann zu importieren. Das würde das Build-Präfix entfernen. Verwenden Sie dann den Handler, um das erforderliche Präfix hinzuzufügen. Ich bin mir nicht sicher, wie ich die go-Datei, die die Einbettung durchführt, von der Einbettung selbst ausschließen kann. Siehe https://github.com/golang/go/issues/41191#issuecomment -686621090

Es wurde vorher vorgeschlagen, die Dateien direkt einzubetten, dh in das Build-Verzeichnis zu tun und es dann zu importieren. Das würde das Build-Präfix entfernen. Verwenden Sie dann den Handler, um das erforderliche Präfix hinzuzufügen. Ich bin mir nicht sicher, wie ich die go-Datei, die die Einbettung durchführt, von der Einbettung selbst ausschließen kann. Siehe #41191 (Kommentar)

Das ist leider nicht gut für Verzeichnisse, die von anderen Tools erstellt wurden, zB die Ausgabe von beispielsweise einem Webpack-Build oder CRA (wo sie oft vorher bereinigt und nicht eingecheckt werden). Ich würde lieber die Dateinamen hacken.

Es wurde vorher vorgeschlagen, die Dateien direkt einzubetten, dh in das Build-Verzeichnis zu tun und es dann zu importieren. Das würde das Build-Präfix entfernen. Verwenden Sie dann den Handler, um das erforderliche Präfix hinzuzufügen. Ich bin mir nicht sicher, wie ich die go-Datei, die die Einbettung durchführt, von der Einbettung selbst ausschließen kann. Siehe #41191 (Kommentar)

Das ist leider nicht gut für Verzeichnisse, die von anderen Tools erstellt wurden, zB die Ausgabe von beispielsweise einem Webpack-Build oder CRA (wo sie oft vorher bereinigt und nicht eingecheckt werden). Ich würde lieber die Dateinamen hacken.

Wenn Sie ein so großes Plugin-System wie Webpack verwenden, können Sie einfach ein weiteres Webpack-Plugin installieren, um das embed.go entlang der Assets selbst zu generieren. Wenn Sie etwas einfacheres mit einem Makefile oder Shell-Skript verwenden, ist es auch einfach, die .go-Datei von dort aus zu generieren.

@zikaeroh

im Gegensatz zu einem verallgemeinerten "Gib mir ein FS, das ein Unterverzeichnis eines anderen FS ist", was mit optionalen Methoden nicht einfach ist

Es soll einfach sein. Die Handhabung des Verpackungsproblems war Teil des Designprozesses. Insbesondere soll der Wrapper alle optionalen Methoden implementieren, indem er einfach die entsprechende Hilfsfunktion in fs aufruft. Wenn das nicht funktioniert, ist das besorgniserregend und es wäre toll, ein paar Details zu erfahren.

Außerdem, IMO, sollte eine Implementierung eines solchen Wrappers (der ein Präfix entfernt) letztendlich von io/fs bereitgestellt werden (analog zu io.LimitWriter usw.). Ich würde annehmen, dass der einzige Grund, warum es noch nicht passiert ist, die Zeit ist.

@andig Das Problem dabei ist, dass die Go-Datei, die die Einbettungs-Direktive und die Variable enthält, dann auch vom FS aus sichtbar ist (und von HTTP bedient würde oder auf andere Weise exponiert werden könnte).

Das Problem dabei ist, dass die Go-Datei, die die Einbettungs-Direktive und die Variable enthält, dann auch vom FS aus sichtbar ist (und von HTTP bedient würde oder auf andere Weise exponiert werden könnte).

Eine Möglichkeit, dies zu beheben, könnte darin bestehen, die Möglichkeit hinzuzufügen, bestimmte Dateien/Ordner von der Einbettung auszuschließen ( @rsc ?)

Eine Möglichkeit, dies zu beheben, könnte darin bestehen, die Möglichkeit hinzuzufügen, bestimmte Dateien/Ordner von der Einbettung auszuschließen ( @rsc ?)

Der Vorschlag wurde vor über einem Monat angenommen und ist bereits umgesetzt; Ich glaube nicht, dass große Designänderungen wie das Ausschließen von Pfaden an dieser Stelle sinnvoll sind. Wenn Sie ein Problem mit dem implementierten Design haben, das Sie nicht umgehen können, würde ich vorschlagen, einen separaten Fehlerbericht mit Details einzureichen, der dann vor der endgültigen Version 1.16 nachverfolgt werden kann.

@Merovius

Es soll einfach sein. Die Handhabung des Verpackungsproblems war Teil des Designprozesses. Insbesondere soll der Wrapper alle optionalen Methoden implementieren, indem er einfach die entsprechende Hilfsfunktion in fs aufruft. Wenn das nicht funktioniert, ist das besorgniserregend und es wäre toll, ein paar Details zu erfahren.

Wie würde das Entfernen von Präfixen für Glob funktionieren?

@icholy Ich

func (f *stripFS) Glob(pattern string) (matches []string, err error) {
    matches, err = fs.Glob(f.wrapped, path.Join(f.prefix, pattern))
    for i, m := range matches {
        matches[i] = strings.TrimPrefix(m, f.prefix+"/")
    }
    return matches, err
}

Vielleicht mit etwas zusätzlicher Sorgfalt.

Wenn ich damit auf gotip spiele, stelle ich fest, dass es .DS_Store-Dateien enthält. Dies ist ziemlich unvermeidlich, denke ich, aber ich mache mir Sorgen, dass das Einschließen von Punktdateien zu einer versehentlichen Aufnahme von Dateien führt. Vielleicht sollten die Docs eine starke Warnung diesbezüglich haben?

Meine Shell enthält keine Punktdateien in * , also wenn ich sie einschließen möchte, muss ich * .* . Dies könnte eine (vielleicht ebenso überraschende) Möglichkeit sein, ein gewisses Maß an Kontrolle zu geben.

Ich bin mir nicht sicher, was ich denken soll - IMO, Punktdateien sollten durch das Muster nicht wirklich anders behandelt werden, aber OTOH, das .DS_Store Beispiel scheint etwas zu sein, das wirklich angesprochen werden sollte.

Warum nicht einfach git clean -dffx && go build ? Wenn sich die DS_Store-Dateien in git befinden, werden sie in den Build aufgenommen. Wenn sie es nicht sind, werden sie es nicht sein. Dies funktioniert auch gut mit gitignore.

Sie sollten sowieso mit einem sauberen VCS-Checkout bauen. Wenn Sie zufällige temporäre Dateien hinzufügen, schaffen sie es möglicherweise in den endgültigen Build und Sie können unmöglich wissen, mit welchen Dateien die Leute enden werden. Vielleicht möchten wir dies dokumentieren.

@mvdan Ich stimme im Prinzip zu, aber in der Praxis befürchte ich, dass sich viele Leute an schmutzigen Builds verbrennen, wenn sie nicht gewarnt werden. Ich möchte kein geheimes Leck sehen, von dem jemand nicht wusste, dass seine .env-Datei versehentlich eingebettet wurde. Ich habe unzählige Beispiele für einen gleichwertigen Fehler beim PHP-Hosting gesehen, obwohl er leicht hätte verhindert werden können, indem man Apache anweist, Punktdateien auszuschließen.


Betreff: http.FileServer einbetten

Wenn Sie ein so großes Plugin-System wie Webpack verwenden, können Sie einfach ein weiteres Webpack-Plugin installieren, um das embed.go entlang der Assets selbst zu generieren.

Das stimmt, aber es ist sehr fummelig. Der Sinn von embed.FS besteht darin, den Bedarf an verrückten Makefile-Workarounds zu reduzieren. Ich denke, die einfache Lösung besteht darin, fs.WithPrefix(string) fs.FS , das ein FS in einem Unterverzeichnis sperrt. Ich dachte, es gäbe eine Diskussion darüber im Vorschlag, aber ich kann sie jetzt nicht finden. Vielleicht wurde es nur auf Reddit oder so diskutiert.

Eine Möglichkeit, dies zu beheben, könnte darin bestehen, die Möglichkeit hinzuzufügen, bestimmte Dateien/Ordner von der Einbettung auszuschließen ( @rsc ?)

Es könnte so etwas sein

//go:embed static
//go:embed-exclude .*
var staticFiles embed.FS

Die embed-exclude-Anweisung könnte einfach einen Glob-Filter für die akzeptierten Dateien durchführen und alle Übereinstimmungen entfernen…

Wenn wir vermeiden möchten, diesem Vorschlag mehr hinzuzufügen, könnte es sich auch um eine Lint-Regel handeln, die eingebettete Dateisysteme auf potenziell unerwartete Punktdateien überprüft und Sie warnt, damit Sie Ihren Build reparieren können, um sie zu entfernen.

Oder schließen Sie Punktdateien standardmäßig aus, es sei denn, sie werden ausdrücklich durch Hinzufügen von .* oder ähnlichem erwähnt.

Das würde sich immer noch nicht darum kümmern, eine Assets.go-Datei offenzulegen. Bezüglich des bereits umgesetzten Vorschlags sei darauf hingewiesen, dass die Frage nach der Vermögensbildung in der Diskussionsphase aufgeworfen wurde. Es ist wahrscheinlich nicht gefährlich, eine (ansonsten leere, außer der Einbettungsrichtlinie) Assets.go freizulegen, aber es wäre sauberer, sie nicht zu haben. Wie üblich gibt es alle möglichen Workarounds, die angewendet werden können.

Es ist wahrscheinlich nicht gefährlich, eine (ansonsten leere, außer der Einbettungsrichtlinie) Assets.go freizulegen, aber es wäre sauberer, sie nicht zu haben.

Ich stimme zu, dass dies sehr unwahrscheinlich ist, aber ich würde es hassen, wenn geschlossener Quellcode aufgrund von Fehlkonfigurationen versehentlich durchgesickert wäre, wenn wir es einfach machen können, die Dinge richtig zu konfigurieren.

Ich möchte kein geheimes Leck sehen, von dem jemand nicht wusste, dass seine .env-Datei versehentlich eingebettet wurde.

Wenn jemand //go:embed static/* und es ein static/.env oder static/.super-secret , würden Sie dann nicht sagen, dass der Benutzer diese Dateien wirklich einschließen wollte? Warum sollten sie sich sonst im statischen Verzeichnis befinden?

Ich verstehe, dass dies den Erwartungen des Benutzers entspricht und was * in den meisten Kontexten bedeutet, aber ich persönlich denke, dass https://golang.org/pkg/path/filepath/#Glob-Semantik unser einziges Gut ist Möglichkeit. Es ist das einfachste und was die meisten Go-Benutzer im Zusammenhang mit der Entwicklung von Go gewohnt sind.

Ich denke jedoch, vor den Gefahren der Einbettung von * warnen, ist in jedem Fall eine gute Idee, da man in einer beträchtlichen Anzahl von Fällen die Fehlerwahrscheinlichkeit reduzieren könnte, indem man spezifischere Globs wie *.png .

Außerdem ermutige ich Sie immer noch, ein separates Problem einzureichen, das im Hinblick auf die Version 1.16 verfolgt werden kann und aus der Sicht eines Fehlerberichts geschrieben wurde. Dieser Vorschlag wird angenommen und umgesetzt, daher gehe ich davon aus, dass er sehr bald geschlossen wird. Sie könnten den Fehlerbericht beispielsweise so formulieren: Die Unterstützung eingebetteter Dateien führt leicht dazu, dass unbeabsichtigte Dateien eingefügt werden (und geben Sie einige Beispiele an).

Wenn jemand //go:embed static/* verwendet und es ein static/.env oder static/.super-secret gibt, würden Sie dann nicht sagen, dass der Benutzer diese Dateien wirklich einschließen wollte? Warum sollten sie sich sonst im statischen Verzeichnis befinden?

Es gibt eine große Anzahl von Eckfällen, zum Beispiel haben Sie eine Editiersitzung mit vim geöffnet, aber nicht geschlossen, und es hat eine .*.swp Datei mit einigen Inhalten erstellt, die niemand sehen soll.

Diskussion zu #42321 verschieben.

Dies würde das Prisma-Team für unseren Database Go-Client sehr begrüßen , da wir zur Laufzeit eine Abfrage-Engine verwenden, die in Rost geschrieben ist und irgendwie in der erstellten go-Binärdatei enthalten sein muss.

Derzeit packen wir die Binärdatei zum Zeitpunkt go:generate in eine .go-Datei, aber die Dateigröße ist viel höher als bei einer binären .gz-Datei.

Native Einbettungen würden dies viel besser machen, sodass wir eine .gz-Datei direkt in die endgültige go-Binärdatei einbetten könnten.

Wenn jemand //go:embed static/* und es ein static/.env oder static/.super-secret , würden Sie dann nicht sagen, dass der Benutzer diese Dateien wirklich einschließen wollte?

Nein, würde ich nicht.

$ mkdir z
$ touch z/.secret z/intended
$ ls z/*
z/intended
$ ls z
intended

Siehe meinen späteren Kommentar in https://github.com/golang/go/issues/42328#issuecomment -720169922.

Ich liebe die Idee, statische Dateien / Vorlagen einbettbar zu machen, was definitiv die Produktivität der Go-Entwicklung erheblich steigern kann.

Aber sollten wir ein anderes Tag (zB @ oder was auch immer) erneuern, als dieses // wiederzuverwenden, das Kommentare sein sollen?

Bis jetzt wurde das // überstrapaziert, denke ich, denken Sie an diese:

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:webhook:verbs=create;update,path=/validate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,versions=v1,name=vcronjob.kb.io
// +optional
...

Aber sollten wir ein anderes Tag (zB @ oder was auch immer) erneuern, als dieses // wiederzuverwenden, das Kommentare sein sollen?

Das ist eine separate Diskussion, und obwohl es schön wäre, wenn Direktiven keine Kommentare wären, verlässt sich ein Großteil des go1-Kompatcodes bereits darauf, also wird er sich höchstwahrscheinlich nicht ändern.

Aber sollten wir ein anderes Tag (zB @ oder was auch immer) erneuern, als dieses // wiederzuverwenden, das Kommentare sein sollen?

Ich habe versucht, es zu finden und bin gescheitert, aber ich erinnere mich, dass es eine Entscheidung gab, für diese Art von Direktiven auf //go:<word> zu standardisieren.
Es scheint keine gute Idee zu sein, die Syntax zu ändern, da man sich entschieden hat, sie ausdrücklich zu konvergieren.
(Außerdem sind es natürlich Kommentare speziell, damit der Compiler sie ignoriert - diese Direktiven sind spezifisch für das go-Tool, daher sollten sie nicht in die eigentliche Sprache eindringen)

Ich habe eine Erwähnung zum Thema io/fs , dass embed.FS ETag unterstützt: https://github.com/golang/go/issues/41190#issuecomment -737702433

Ich habe versucht, einen Test dafür auszuführen, aber ich sehe keinen ETag Antwortheadersatz. Vielleicht verstehe ich die Verwendung falsch. Muss ich damit rechnen, hier einen zu sehen? https://play.golang.org/p/Wq5xU5blLUe

Ich glaube nicht. http.ServeContent (verwendet von http.FileServer ) überprüft den ETag-Header, legt ihn jedoch nicht fest, AIUI.

In einem Kommentar oben hat Russ gesagt, dass ETag zum Laufen gebracht wird . Die Schwierigkeit besteht darin, embed.FS http.FileServer die notwendigen Informationen mitzuteilen, um ETag oder andere Caching-Header zu setzen. Es sollte wahrscheinlich ein separates Tracking-Problem dafür geben.

Persönlich würde ich argumentieren, dass embed.FS die Zeit des letzten Commits des entsprechenden Moduls als ModTime . Dies würde in etwa debug.BuildInfo entsprechen, würde also die Reproduzierbarkeit nicht beeinträchtigen. Obwohl ich mir nicht sicher bin, wie dies für Commits festgelegt wird, die keinem markierten Release entsprechen, bleibt die Frage, wie es für Builds aus schmutzigen Worktrees eingestellt werden soll.

Aber ich vertraue darauf, dass @rsc eine gute Lösung im Sinn hat :)

Ich bin mir nicht sicher, ob ich den Kommentar zu "Zeit festschreiben" verstehe; Wenn die Modulquelle eine ZIP-Datei ist, gibt es kein "Commit". Ich sehe keine Zeiten, die in debug.BuildInfo oder debug.Module erwähnt werden .

Noch wichtiger ist, dass ich argumentieren würde, dass jeder zeitstempelbasierte Mechanismus einem richtigen (inhaltsbasierten) Etag streng unterlegen ist.

@tv42 Jede Modulversion ist entweder a) eine von einem Tag abgeleitete semantische Version (die auf einen Commit zeigt) oder b) eine Pseudoversion, die einen Commit-Hash enthält. Ich denke? Zumindest in git. Vielleicht verstehe ich etwas falsch.

Noch wichtiger ist, dass ich argumentieren würde, dass jeder zeitstempelbasierte Mechanismus einem richtigen (inhaltsbasierten) Etag streng unterlegen ist.

Ich bin mir nicht sicher. Es benötigt entweder einen Seitenkanal, um den Hash zu kommunizieren, oder der Server muss den Hash der Datei auf Anfrage berechnen (was ziemlich teuer erscheint). Schließlich weiß net/http a priori nicht, ob sich der Inhalt von fs.FS ändern kann. Das Endergebnis eines Hash-basierten ETags könnte die Kosten für das Hinzufügen eines solchen Seitenkanals (wie einer optionalen Schnittstelle) rechtfertigen, aber es scheint offensichtlich nicht unbedingt besser zu sein.

Außerdem würde ich argumentieren, dass die Unterstützung zumindest auch eines zeitbasierten Ansatzes bedeuten würde, dass Sie mit mehr Kunden arbeiten können. Ich habe jedoch keine Daten, die das unterstützen (dh ich weiß nicht, ob und wie viele Clients es gibt, die If-Modified-Since aber nicht ETag und umgekehrt).

Aber eigentlich ist es mir egal, welcher Ansatz gewählt wird. Ich wollte nur die Möglichkeit erwähnen, den Zeitpunkt zu verwenden, an dem eine Modulversion getaggt wurde.

@Merovius Go-Module werden in Form von ZIP-Dateien angegeben. Die Tatsache, dass Git eine gängige Quelle zum Konstruieren ist, ändert nichts an der Spezifikation. Die Reißverschlüsse haben null Zeitstempel:

$ unzip -v ~/go/pkg/mod/cache/download/golang.org/x/crypto/@v/v0.0.0-20200510223506-06a226fb4e37.zip|head
Archive:  /home/tv/go/pkg/mod/cache/download/golang.org/x/crypto/@v/v0.0.0-20200510223506-06a226fb4e37.zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
     345  Defl:N      233  33% 1980-00-00 00:00 237856c8  golang.org/x/[email protected]/.gitattributes

Es scheint einen Zeitstempel in der begleitenden *.info Datei zu geben, aber ich habe keine Ahnung, ob dies zuverlässig oder für die Tools verfügbar ist.

$ cat ~/go/pkg/mod/cache/download/golang.org/x/crypto/@v/v0.0.0-20200510223506-06a226fb4e37.info; echo
{"Version":"v0.0.0-20200510223506-06a226fb4e37","Time":"2020-05-10T22:35:06Z"}

Welcher Zeitstempel sollte selbst dann lauten: embed use im Hauptmodul (das, in dem Sie go build ausführen)?

Persönlich scheint Hashing im Kontext statischer Dateien, die unveränderlich in die Binärdatei eingebettet sind, den Zeitstempeln in jeder Hinsicht überlegen zu sein (außer der Unterstützung von Legacy-Dateien aus der Zeit vor ETag, vor HTTP/1.1, den 90er Jahren) und hat keine mögliche Quelle für Verwirrung in verteilten Systemen und gleichzeitigen Builds. Ich bezweifle ernsthaft, dass es Clients gibt, die ETag nicht mehr verstehen, und sie werden den Übergang zu HTTP/2 sicher nicht schaffen.

Ein Seitenkanal zur Kommunikation des Hash klingt für mich nach dem Richtigen; eine optionale FS-Schnittstelle, um einen Hash der Datei zurückzugeben. (Und vielleicht ein anderer, um einen bestimmten Hash-Algorithmus anzufordern, wenn sie ihn haben?. Einige Anwendungsfälle benötigen wirklich speziell sha256/sha1/md5, nicht nur "etwas Hash"). Ein FS, das keine billige Antwort hat, kann sich dafür entscheiden, sie nicht zu implementieren.

(Obwohl moderne Hashes Gigabyte/Sekunde/Kern sind, selbst wenn sie kryptographisch sicher sind, Dutzende von Gigabyte/Sekunde für weniger sichere und basierend auf Statistikaufrufen einfach zwischenzuspeichern. Unterstützen Sie einfach ETag überall, Leute.)

Ich habe heute über diesen Vorschlag nachgedacht. Ich freue mich, dass diese Funktionalität in der Go-Toolchain enthalten ist, und ich schätze all die Gedanken und Bemühungen, die bisher in den Vorschlag gesteckt wurden. Danke, dass Sie dies vorgeschlagen und vorangetrieben haben.

Ich habe zwei Fragen und dann einen Vorschlag (ich weiß zu schätzen, dass die Vorschlagsdiskussion abgelaufen ist und bereits die Veröffentlichung eingefroren ist):

Zulässige Typen

Mir ist aufgefallen, dass der aktuelle Code erwartet, dass die Variablen genau wie ~ embed.FS ~, string oder []byte deklariert werden. Insbesondere sind diese nicht erlaubt: []uint8 , ~ FS nach dem Dot-Import von "embed"~ oder alle anderen identischen Typen, die mit Typaliasen konstruiert wurden. (Bearbeiten: Ich habe vergessen, wie Punktimporte in gc funktionieren, und den Code zum Erkennen von embed.FS falsch gelesen.)

Ist das beabsichtigt oder ein Bug?

Semantik von []byte -typisierten Variablen.

Ich sehe keine Erwähnung der erwarteten Identitätssemantik von []byte -typisierten Variablen. Insbesondere für funktionsbezogene Variablen. Dies ist für string - und embed.FS -typisierte Variablen nicht wichtig, da sie auf unveränderliche Daten verweisen, die sicher dedupliziert werden können. Es ist jedoch wichtig, die beabsichtigte Semantik für []byte -typisierte Variablen zu kennen.

Bei der aktuellen Implementierung gibt das folgende Testprogramm false true (wenn foo.txt nicht leer ist). Ist das beabsichtigt/garantiert?

package main

//go:embed foo.txt
var a []byte

//go:embed foo.txt
var b []byte

func f() *byte {
    //go:embed foo.txt
    var x []byte
    return &x[0]
}

func main() {
    println(&a[0] == &b[0], f() == f())
}

Ich denke, die mehr Go-ähnliche Semantik für //go:embed Variablen wäre die von zusammengesetzten Literalen: dass jede Ausführung eine neue Kopie ergibt.

Wenn es keinen Konsens über die richtige Semantik dafür gibt, können wir immer darauf setzen und funktionsbezogen machen, []byte -typed bettet einen Fehler für Go 1.16 ein: Benutzer können immer noch entweder eine Variable string -typisierte Variable und konvertieren Sie sie in []byte wenn sie eine zusammengesetzte Literal-Semantik wünschen. Wir könnten dann später noch einmal überprüfen, von welchem ​​Verhalten die Benutzer mehr profitieren würden.

Weitere //go: Anweisungen vermeiden

Ich rate davon ab, //go: Direktiven für Endbenutzer hinzuzufügen, die sich auf die Programmsemantik auswirken, und ich finde die Argumente für die Bevorzugung von //go:embed gegenüber der normalen Go-Syntax nicht zwingend. Ich ermutige Sie respektvoll, diese Entscheidung vor der Veröffentlichung von Go 1.16 zu überdenken. (Wieder weiß ich zu schätzen, wie spät diese Anfrage ist.)

Ich beginne damit, darauf hinzuweisen, dass ich eine funktionierende Proof-of-Concept-CL unter CL 276835 habe , die die Einbettungs-API ändert von:

//go:embed foo.txt bar.txt
var x embed.FS

zu

var x = embed.Files("foo.txt", "bar.txt")

Ebenso stehen die Funktionen embed.Bytes und embed.String zur Verfügung, um eine einzelne Datei einzubetten und als []byte oder string Wert abzurufen.

Antworten

Ebenso kann eine embed.Files-Variable eine globale oder eine lokale Variable sein, je nachdem, was im Kontext bequemer ist.

Mit embed.Files usw. können Sie sie auch in Ausdruckskontexten verwenden, was noch bequemer sein kann.

Es ist ein Fehler, //go:embed in einer Quelldatei zu verwenden, die "embed" nicht importiert (die einzige Möglichkeit, diese Regel zu verletzen, besteht in Typ-Alias-Tricks).

Bei embed.Files usw. ist dies ein Fehler, da die Funktionen per Paketeinbettung exportiert werden.

Goimports (und gopls usw.) kann diese Regel beigebracht werden und der Import wird bei Bedarf automatisch mit einem //go:embed in jede Datei eingefügt.

Es ist keine spezielle goimports- oder gopls-Logik erforderlich, um zu wissen, dass das Importieren von "embed" der richtige Weg ist, um die Verwendung von embed.Files usw.

Dieser Ansatz behebt das Problem der Typprüfung – es handelt sich nicht um eine vollständige Sprachänderung –, weist jedoch immer noch eine erhebliche Implementierungskomplexität auf.

Insbesondere ist CL 276835 eine Nettoentfernung von Code. Insbesondere der Compilercode (der in gccgo und anderen Compilern neu implementiert werden muss) ist viel einfacher.

Ich gehe auch davon aus, dass es einfacher sein wird, go/types die nuancierte Semantik von embed.Files usw. beizubringen (dh dass sie nur String-Literalargumente akzeptieren), als //go:embed .

Der Befehl go müsste die gesamte Go-Quelldatei analysieren, um zu verstehen, welche Dateien zum Einbetten verfügbar gemacht werden müssen. Heute wird nur bis zum Importblock geparst, nie vollständige Go-Ausdrücke

Bei CL 276835 ist das Verhalten das gleiche wie bei tip: Der go-Befehl parst die gesamte Go-Quelldatei auf Dateien, die Paketeinbettung importieren, und nur die Importe auf Dateien, die dies nicht tun.

Zugegeben, für Dateien, die embed importieren, führt CL 276835 ein vollständiges Parsen und Walken durch, während tip einen effizienteren String-Scan für //go:embed Kommentare durchführt. Ich denke, ein optimierter One-Pass-Algorithmus zum Finden von embed.Files Anrufen ist jedoch machbar, wenn dies gewünscht wird.

Es wäre den Benutzern auch unklar, welche Einschränkungen den Argumenten dieser speziellen Aufrufe auferlegt werden: Sie sehen aus wie normale Go-Aufrufe, können aber nur String-Literale akzeptieren, keine von Go-Code berechneten Strings und wahrscheinlich nicht einmal benannte Konstanten (oder sonst die go-Befehl würde einen vollständigen Go-Ausdrucksauswerter benötigen).

Dies scheint mir nicht wesentlich von der Direktive //go:embed zu unterscheiden: Benutzer haben keine Erwartung, welche Argumente sie dort verwenden dürfen, bis sie sie zum ersten Mal verwenden. Darüber hinaus erhalten Benutzer in beiden Fällen Compiler-Fehlermeldungen, wenn sie es falsch verwenden, aber IDEs und andere Tools bieten automatisch bessere Godocs und Querverweise für embed.Files usw.

@mdempsky FS nach einem Punktimport von embed ist der gleiche Typ. Dieser spezielle Fall scheint also ein einfaches "Ja, das ist in Ordnung" (versucht und es funktioniert bereits). Ebenso ist byte ein Alias ​​für uint8 , also ist []uint8 auch der gleiche Typ wie []byte , obwohl dies etwas überraschenderweise gerade nicht funktioniert . Ich würde jedoch argumentieren, dass die implementierte Semantik vorerst in Ordnung ist - wir können später immer mehr Typen und / oder Aliase und / oder sogar "den gleichen zugrunde liegenden Typ" zulassen, wenn dies erforderlich ist.

Ich denke, die mehr Go-ähnliche Semantik für //go:embed-Variablen wäre die von zusammengesetzten Literalen: dass jede Ausführung eine neue Kopie ergibt.

Ich stimme eher zu, das war auch meine Erwartung. Zuerst dachte ich, dies könnte ein Artefakt der Escape-Analyse sein und der Compiler erkennt, dass Sie die Daten nicht ändern, aber nein:

func f() {
    //go:embed foo.txt
    var x []byte
    fmt.Printf("%q\n", x)
    x[0] = 'x'
}

func main() {
    f()
    f()
}

Allerdings hat "jede var-Deklaration eine neue Variable" auch seine Probleme, denn es bedeutet Code wie diesen

func ServeIndex(w http.ResponseWriter, r *http.Request) {
    //go:embed "index.html"
    var x []byte
    http.ServeContent(w, r, "index.html", time.Now(), bytes.NewReader(x))
}

unnötig zuordnen und kopieren würde. Vielleicht ist das in Ordnung und wird durch die Vorteile einer klareren Semantik aufgewogen. Aber vielleicht ist es auch ein Zeichen dafür, lokale []byte Einbettungen zu verbieten, wie Sie sagen.

Ich gehe auch davon aus, dass es einfacher sein wird, go/types die nuancierte Semantik von embed.Files usw. beizubringen (dh, dass sie nur String-Literalargumente akzeptieren), als //go:embed.

Im Go-Typ-System können zumindest nur String-Konstanten durchgeführt werden.

Aber muss go/types überhaupt über eingebettete Dateien mit //go:embed Bescheid wissen? Der Typ dieser Variablen ist schließlich ziemlich klar (mit Ausnahme der lokalen []byte Einbettungen, wie besprochen).

//go:embed "foo"
var x []byte

Soll das wirklich veränderlich sein? Ich kann mir keinen einzigen Anwendungsfall vorstellen, in dem ich ein statisches Asset einbetten, es aber zur Laufzeit mutieren möchte, also habe ich das Gefühl, dass dies nur Fehler ermöglicht. Ich wäre glücklicher gewesen, es in .rodata stecken und in Panik zu geraten, wenn etwas versucht, es zu mutieren.

(Aufgefordert von x[0] = 'x' oben.)

@Merovius schrieb:

@mdempsky FS nach einem Punktimport von embed ist der gleiche Typ. Dieser spezielle Fall scheint also ein einfaches "Ja, das ist in Ordnung" (versucht und es funktioniert bereits).

Vielen Dank. Ich habe vergessen, wie Punktimporte innerhalb des Compilers funktionieren, also habe ich den Code falsch gelesen. Ich hätte es zuerst versuchen sollen, bevor ich dieses Beispiel einfüge.

Ich würde jedoch argumentieren, dass die implementierte Semantik vorerst in Ordnung ist - wir können später immer mehr Typen und / oder Aliase und / oder sogar "den gleichen zugrunde liegenden Typ" zulassen, wenn dies erforderlich ist.

[]byte und []uint8 sind identische Typen unter der Go-Spezifikation, ebenso wie unzählige andere Typen, die mit Typaliasen konstruiert wurden. Wenn es für einen Compiler nicht akzeptabel ist, embed.Files("foo" + ".txt") anders zu behandeln als embed.Files("foo.txt") , dann sollte dasselbe Argument dahingehend erweitert werden, dass Alias-Typen nicht anders behandelt werden dürfen.

Aber muss go/types überhaupt über eingebettete Dateien mit //go:embed Bescheid wissen?

Nein, das muss es nicht, genauso wenig wie es die spezielle Semantik von embed.Files . Aber für beide Syntaxen kann es immer noch von Vorteil sein, dass go/types in der Lage ist, vor Missbrauch zu warnen.

--

@tv42 schrieb:

Soll das wirklich veränderlich sein?

Offensichtlich. Der Go-Compiler sorgt speziell dafür, dass string und embed.FS Einbettungen dedupliziert und in Rodata eingefügt werden, während []byte Einbettungen nicht:

https://github.com/golang/go/blob/56b783ad94f9a55163d494d5b34c783a9603d478/src/cmd/compile/internal/gc/embed.go#L224

Allerdings scheint der Vorschlag diesen Punkt nicht angesprochen zu haben.

Danke für die nachdenklichen Kommentare. Ich habe zwei Probleme zur Nachverfolgung eingereicht:

#43216 - Entfernen Sie die Unterstützung für das Einbetten von Direktiven in lokale Variablen
#43217 - Definiere Aliase vom Typ String und Bytes, die mit //go:embed . verwendet werden müssen

Ich habe kein Problem für die //go:embed-Syntax selbst eingereicht. Ich wollte hier sagen warum, damit es nicht für alle so aussieht, als würde ich das einfach abtun.

Als ich über den Antragsprozess gebloggt habe, habe ich lange nach Wegen gesucht, Entscheidungen in großen Gruppen (und nicht) zu handhaben. Eine für mich sehr sinnvolle Quelle war John Ousterhouts Beitrag „ Open Decision-Making “. Der ganze Beitrag ist lesenswert, aber ich zitiere hier nur den Abschnitt zur Neuüberlegung:

Die letzte Regel für eine offene Entscheidungsfindung besteht darin, sicherzustellen, dass Sie eine Entscheidung nicht überdenken, es sei denn, es liegen wichtige neue Informationen vor. Diese Regel besteht aus zwei Teilen. Erstens müssen Sie bereit sein, Entscheidungen zu korrigieren, die sich als erheblich falsch herausstellen. Dies gilt insbesondere für Startups: Viele Entscheidungen müssen ohne vollständige Informationen getroffen werden und einige davon werden sich zwangsläufig als falsch erweisen. Eine falsche Entscheidung, die schnell korrigiert wird, verursacht wenig oder keinen Schaden, eine falsche Entscheidung, die nicht korrigiert wird, kann jedoch katastrophal sein.

Andererseits sollten Sie eine Entscheidung nicht überdenken, es sei denn, seit der ursprünglichen Entscheidung sind wesentliche neue Informationen bekannt geworden. Wenn keine neuen Informationen verfügbar sind, führt das Überdenken einer Entscheidung wahrscheinlich zum gleichen Ergebnis wie die ursprüngliche Entscheidung, wodurch alle Zeit verschwendet werden. Es ist nicht ungewöhnlich, dass Mitarbeiter Wochen nach einer Entscheidung zu mir kommen: „John, ich habe gegen die Entscheidung zu XYZ gestimmt, und je länger ich darüber nachdenke, desto mehr bin ich überzeugt, dass es falsch war überdenke das nochmal." Meine Antwort ist "Welche neuen Informationen haben Sie?" Wenn die Antwort "keine" lautet, überdenken wir es nicht. In solchen Situationen ist es hilfreich, die während der Diskussion vorgebrachten Argumente aufzuzeichnen, damit Sie überprüfen können, ob neue Informationen wirklich neu sind. Wenn Sie es zu einfach machen, Entscheidungen wieder zu öffnen, schwanken Sie am Ende hin und her, wo keine Entscheidung endgültig ist und Mitarbeiter zögern, Entscheidungen umzusetzen, weil sie nicht glauben, dass sie dauerhaft sind.

Wenn Sie sicherstellen möchten, dass Sie nicht viele Entscheidungen noch einmal überdenken, sollten Sie während des Entscheidungsprozesses viel Input sammeln. Wenn Sie nicht genügend Eingaben erhalten, erhöhen Sie die Wahrscheinlichkeit, dass nach der Entscheidung erhebliche neue Eingaben auftreten, was bedeutet, dass Sie es sich noch einmal überlegen müssen. Wenn Sie gute Arbeit beim Sammeln von Input leisten, ist es viel unwahrscheinlicher, dass Sie Ihre Entscheidungen überdenken müssen.

Das hat mich wirklich überzeugt: Der Go-Proposal-Prozess (wie große Open-Source-Projekte im Allgemeinen) ist ein System mit einer weitaus höheren angebotenen Last als Arbeitskapazität, daher ist es wichtig, dass wir (wenn nötig nicht einverstanden und) uns verpflichten und zur nächsten Entscheidung übergehen .

string und []byte wurden als Reaktion auf die ersten Rückmeldungen relativ spät im Prozess der Prüfung dieses Problems hinzugefügt, und wir haben offensichtlich nicht alle Auswirkungen durchdacht. Also reichte ich diese beiden neuen Ausgaben Nr. 43216 und Nr. 43217 ein.

Andererseits war die //go:embed-Syntax ein Kernstück der ursprünglichen Diskussionen und wurde ausführlich diskutiert, sowohl Vor- als auch Nachteile. Ich glaube nicht, dass es „bedeutende neue Informationen“ gibt, die uns dazu veranlassen sollten, diese Syntax vollständig zu überdenken. Um also voranzukommen und Ousterhouts Ratschläge zur Neuüberlegung im Hinterkopf zu behalten, lasse ich dies beiseite.

Nochmals vielen Dank für den Hinweis auf die String- und []Byte-Probleme!

Offensichtlich. Der Go-Compiler sorgt speziell dafür, dass string und embed.FS Einbettungen dedupliziert und in Rodata eingefügt werden, während []byte Einbettungen nicht:

@rsc hast du zufällig an diesen letzten Punkt gedacht? Bei der aktuellen Implementierung könnte es zur "Best Practice" werden, Zeichenfolgen anstelle von []Byte zu verwenden, um bessere Binärdateien zu erstellen. Sind wir damit einverstanden?

Ich verstehe nicht, warum das eine "Best Practice" ist. Für mich ist das so, als ob es "Best Practice" ist, Sentinel-Fehler-Konstanten zu machen. Ergo sollten wir keine paketbezogenen Variablen vom Typ error zulassen - insofern, als ich sowohl damit nicht einverstanden bin, dass dies eine gute Praxis ist, als auch mit der zusätzlichen Einschränkung als a Lösung.

Ich konnte ein Argument sehen, nur string in lokalen Variablen zu verwenden. Aber in paketbezogenen Variablen ist die Semantik klar und gut definiert, und ich würde nicht mehr davon abraten, eine []byte Einbettung zu verwenden, als ich von der Verwendung jeder anderen []byte Variablen abraten würde.

@mvdan , wenn die Leute dazu string anstelle von []byte als Best Practice zu verwenden, würde ich das als eine gute Sache betrachten. string ist der geeignete Go-Typ für „eine unveränderliche Zeichenfolge von Bytes“, während []byte der geeignete Typ für „eine veränderliche Zeichenfolge von Bytes“ oder „eine Zeichenfolge von Bytes mit unbestimmter Veränderlichkeit“ ist.

In den Fällen, in denen Sie einen Wert vom Typ string (unveränderlich) haben und ihn als Typ []byte (unbestimmt) verwenden möchten, können Sie bereits unsafe um dies korrekt zu tun . (Siehe zum Beispiel mein unsafeslice.OfString ). Vielleicht sollten wir für diesen Vorgang eine unterstützte Standardbibliothek hinzufügen, aber das scheint ein separater Vorschlag zu sein.

Es scheint also in Ordnung zu sein, immer den Typ string wenn der Wert wirklich schreibgeschützt sein soll.

@Merovius @bcmills Sie bringen gute Punkte, und um das

Ich glaube nicht, dass die Deduplizierung in der Praxis viel auftauchen wird. (In welcher Einstellung werden mehrere Pakete genau die gleichen Dateien einbetten?) Die Leute sollten die Form verwenden, die sie brauchen, und sich keine Sorgen machen "String bedeutet, dass meine Binärdateien kleiner sind", denn im Allgemeinen glaube ich nicht, dass dies der Fall sein wird.

Ein paar Leute fragten nach ETags. Wir haben dafür keine Zeit mehr, aber ich habe einen Vorschlag unter https://github.com/golang/go/issues/43223 eingereicht und hoffentlich führt das zu einer guten Idee, die in Go 1.17 einfließen kann. Tut mir leid, dass ich es in dieser Runde nicht bekommen kann.

Vielen Dank für die Einreichung von #43216 und #43217. Wenn diese akzeptiert werden, werden sie meine noch offenen Bedenken mit dem //go:embed Vorschlag im Wesentlichen ausräumen.

Andererseits war die //go:embed-Syntax ein Kernstück der ursprünglichen Diskussionen und wurde ausführlich diskutiert, sowohl Vor- als auch Nachteile. Ich glaube nicht, dass es „bedeutende neue Informationen“ gibt, die uns dazu veranlassen sollten, diese Syntax vollständig zu überdenken. Um also voranzukommen und Ousterhouts Ratschläge zur Neuüberlegung im Hinterkopf zu behalten, lasse ich dies beiseite.

Ich kann es respektieren, Dinge, die bereits durch ausführliche Diskussionen beschlossen wurden, nicht noch einmal aufgreifen zu wollen. Aber nach Durchsicht der Diskussion, die in #35950, dem Reddit-Thread und hier stattfand, glaube ich nicht, dass sie die Entscheidung rechtfertigen, //go:embed .

Hier sind die Kommentare, die ich gefunden habe, die sich auf die Syntax beziehen, um anzugeben, welche Dateien eingebettet werden sollen:


18 GitHub-Problem und Reddit-Kommentare

  • https://github.com/golang/go/issues/35950#issuecomment -561443566 "Der //go:embed Ansatz führt auch eine andere Ebene der Komplexität ein Der Ansatz "Paket einbetten" scheint für die statische Analyse freundlicher zu sein." (Hinweis: Der überarbeitete Vorschlag von //go:embed erleichtert die Eingabe von Prüfcode, aber es ist immer noch nicht trivial, wenn ein Analysator die verwendeten Zeichenfolgen tatsächlich sehen möchte, da sie nicht in go/ast enthalten sind.)
  • https://github.com/golang/go/issues/35950#issuecomment -561450136 "Das ist ein sehr starkes Argument für die Verwendung eines Pakets. Es macht es auch lesbarer und dokumentierbarer, da wir alles mit regulärem godoc dokumentieren können als tief in der Dokumentation von cmd/go." (Hinweis: Ich denke, dies wird hauptsächlich von #43217 angesprochen, da dann die Paketeinbettungstypen einen einfachen Referenzpunkt bieten.)
  • https://github.com/golang/go/issues/35950#issuecomment -561840094 "Gibt es einen Grund, warum es nicht Teil von go build / link sein könnte... zB go build -embed example=./path/example.txt und ein Paket, das aufdeckt Zugriff darauf (zB embed.File("example") , anstatt go:embed ?" (Schlägt Funktionssyntax über //go: Direktive vor. Abgestimmt, aber ich vermute, weil es vorgeschlagen hat go build Flaggen.)
  • https://github.com/golang/go/issues/35950#issuecomment -561726107 "Ich bin kein großer Fan von Kommentaren, die zu Code kompiliert werden, aber ich finde auch das Pseudo-Paket, das die Kompilierung beeinflusst, etwas zu sein auch seltsam. Wenn der Verzeichnisansatz nicht verwendet wird, ist es vielleicht etwas sinnvoller, eine Art Deklaration der obersten Ebene in die Sprache einzubetten. Es würde ähnlich wie beim Importieren funktionieren, würde aber nur lokale Pfade unterstützen und würde einen Namen benötigen, um ihm zugewiesen zu werden." (Mag //go: oder neue eingebaute Funktionen nicht, bevorzugt aber immer noch Code anstelle von Kommentaren.)
  • https://github.com/golang/go/issues/35950#issuecomment -561856033 "Außerdem, da //go:generate Direktiven bei go build nicht automatisch ausgeführt werden, kann das Verhalten von go build etwas inkonsistent erscheinen: //go:embed funktioniert automatisch, aber für //go:generate müssen Sie gogenere manuell ausführen ( //go:generate kann den go get-Flow bereits unterbrechen, wenn es .go-Dateien generiert, die für den Build benötigt werden). " (Der Vollständigkeit halber enthalten, aber wir haben bereits //go: Direktiven, die das Verhalten von go build auch ohne //go:embed beeinflussen und nicht beeinflussen, daher finde ich dieses Argument nicht zwingend.)
  • https://github.com/golang/go/issues/35950#issuecomment -562005821 "Ich würde für das neue Paket im Gegensatz zur Richtlinie stimmen. Viel einfacher in den Griff zu bekommen, einfacher zu handhaben/zu verwalten und viel einfacher zu dokumentieren und zu erweitern. zB Können Sie die Dokumentation für eine Go-Direktive wie "go:generate" leicht finden? Wie sieht es mit der Dokumentation für das Paket "fmt" aus? Sehen Sie, wohin ich damit gehe?" (Wieder meistens angesprochen von #43217.)
  • https://github.com/golang/go/issues/35950#issuecomment -562200553 "Ein Argument für ein Kommentar-Pragma (//go:embed) anstelle eines speziellen Verzeichnisnamens (static/): ein Kommentar lässt uns Betten Sie eine Datei in das Testarchiv für ein Paket (oder das xtest-Archiv) ein, aber nicht die getestete Bibliothek. Der Kommentar muss nur in einer _test.go-Datei erscheinen." (Hinweis: Argument ist gegen spezielles Verzeichnis, nicht speziell für //go:embed ; dasselbe Argument erstreckt sich auf embed.Files Funktionen usw.)
  • https://github.com/golang/go/issues/35950#issuecomment -562235108 "Ich mag den package embed Ansatz für die Verwendung der Go-Syntax"
  • https://github.com/golang/go/issues/35950#issuecomment -562713786 "Der import "C" Ansatz hat bereits einen Präzedenzfall für "magische" Importpfade geschaffen. IMO hat es ziemlich gut geklappt."
  • https://github.com/golang/go/issues/35950#issuecomment -562768318 "Mehrere Leute haben darüber gesprochen, automatisch eine API bereitzustellen, um die eingebetteten Dateien auszulesen, anstatt ein weiteres Signal wie einen magischen Kommentar zu benötigen. Ich denke, das sollte" der Weg zu gehen, da es eine vertraute, programmatische Syntax für den Ansatz bietet. Ein spezielles Paket, möglicherweise runtime/embed wie bereits erwähnt, würde dies erfüllen und würde in Zukunft eine einfache Erweiterbarkeit ermöglichen."
  • https://github.com/golang/go/issues/35950#issuecomment -562959330 "Anstatt Kommentare wiederzuverwenden (die am Ende überall verstreut sind und sich persönlich einfach eklig anfühlen), [... ]." (Schlägt weiter vor, go.mod-Dateien zum Aufzählen eingebetteter Dateien zu verwenden. Um fair zu sein, argumentiert auch: "Dies umgeht Probleme wie die Einschränkung des magischen Pakets, um nur String-Literale als Argumente zuzulassen, bedeutet jedoch, dass es schwieriger ist zu überprüfen, ob die eingebetteten Assets werden tatsächlich überall verwendet oder enden als Eigengewicht.")
  • https://github.com/golang/go/issues/35950#issuecomment -562966654 "Noch eine andere Idee: Anstatt eine neue Art von Verb in go.mod einzubetten, könnten wir eine neue Art von Paket einführen, ein Datenpaket , die wie gewohnt importiert und in go.mod verwendet wird. Hier ist eine Strohmann-Skizze." (Vorzug für Code gegenüber Kommentaren.)
  • https://github.com/golang/go/issues/35950#issuecomment -563156010 "Hier ist etwas, was noch nicht erwähnt wurde: Die API für Go-Quellverarbeitungstools (Compiler, statische Analysatoren) ist genauso wichtig wie die Laufzeit-API . Diese Art von API ist ein Kernwert von Go, der zum Wachstum des Ökosystems beiträgt (wie go/ast / go/format und go mod edit ). [...] Im Fall von ein spezielles Paket, ich sehe nichts zu ändern in go.mod Parsing ( go mod Tools) oder go/ast Parser."
  • https://github.com/golang/go/issues/35950#issuecomment -601748774 Schlägt eine Auflistung der go.res-Ressourcendatei vor. Verwendet Code, keine Kommentare.
  • https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fyv9gxw/ "Warum brauchen wir den //go:embed Kommentar zB binclude macht binclude.Include(filename) um eine Datei/ ein Verzeichnis einzubinden was ungefähr fs.Embed(filename) ?"
  • https://github.com/golang/go/issues/41191#issuecomment -686625127 "Meiner Meinung nach ist das Hinzufügen von syntaktischem Zucker in der Sprache zur Unterstützung dieser Werkzeugänderung eine Sprachänderung. Ich bin sicher, dass dies für andere offensichtlich ist. Aber dies ist praktisch ein Kommentar als Code. Ich bin der Meinung, dass Magie/Zucker die Einfachheit und Lesbarkeit der Sprache beeinträchtigt. Es ist sehr leicht, einen magischen Kommentar zu übersehen, der eine Datei einbettet. "
  • https://github.com/golang/go/issues/41191#issuecomment -690423900 „Ich habe ähnliche Bedenken wie @tristanfisher sind für Eckfälle gedacht und kommen selten im Code vor. Angesichts der Popularität des Einbettens statischer Inhalte in Go-Binärdateien wird //go:embed wahrscheinlich häufiger vorkommen.Vielleicht ist es an der Zeit, eine andere Syntax für den Compiler in Betracht zu ziehen Weisungen?"
  • https://github.com/golang/go/issues/41191#issuecomment -690522509 "Ich teile deine Bedenken bezüglich magischer Kommentare."

Während ich die Kommentare lese, scheinen sie immer die Go-Syntax zu bevorzugen, mit dem Vorbehalt, Änderungen vermeiden zu wollen, die Werkzeuge zerstören. Ich habe niemanden gesehen, der für //go:embed als Schreibweise argumentiert hat, sondern es einfach so akzeptiert, wie es ist. Das Hinzufügen neuer Compiler-Intrinsics mit Abwärtskompatibilitäts-Stubs für Legacy-Typprüfungen entspricht eher der ausdrücklichen Präferenz der Diskussionsteilnehmer als mehr //go: Direktiven.

In Anlehnung an die Daumen hoch / Daumen runter Umfragen in #43216 und #43217:

  • Daumen hoch (👍), wenn Sie neue Compiler-Intrinsics bevorzugen (z. B. CL 276835 ):

    import "embed"
    
    var website = embed.Files("logo.jpg", "static/*")
    

    Weitere Anwendungsbeispiele finden Sie unter

    (Hinweis: Die Argumente müssen String-Literale sein , nicht nur String-Werte. Das heißt, deklarierte String-Konstanten wie const logo = "logo.jpg" oder konstante String-Ausdrücke wie "logo" + ".jpg" sind ebenfalls nicht zulässig. embed.Files usw. können in jedem Kontext verwendet werden, nicht nur zum Initialisieren von Variablen.)

  • Daumen runter (👎), wenn Sie neue Compiler-Direktiven bevorzugen (dh den aktuellen Vorschlag):

    import "embed"
    
    //go:embed logo.jpg static/*
    var website embed.FS
    

@mdempsky Ich habe das Gefühl, dass Russ ziemlich klar war, was nötig wäre, um ihm gegenüber eine Aufhebung der Entscheidung zu rechtfertigen - neue Informationen. Ich denke, das Sammeln früherer Kommentare ist das eindeutig nicht. Keine Beleidigung beabsichtigt.

Es gibt keinen Präzedenzfall für diese neuen Arten von Funktionen, oder? Das heißt, etwas, das wie eine normale Paketfunktion aussieht, aber tatsächlich eine spezielle Art von Builtin ist, das nur auf eine bestimmte Weise aufgerufen werden kann?

Sie sagen "intrinsisch", aber die aktuellen intrinsischen Eigenschaften verhalten sich genau wie normale Go-Funktionen.

x := 10000
_ = bits.RotateLeft64(10, x)

Neue Direktiven so zu gestalten, dass sie wie Go-Funktionen aussehen, aber eine andere (restriktivere) Syntax als Go-Funktionen haben, scheint von meiner Position aus eine schlechte Option zu sein. Ich denke, dass embed in der Spezifikation beschrieben werden müsste, im Gegensatz zu //go: Direktiven.

(Sie können die "nur Literalargumente zulässig" im normalen Go-Code annähern, indem Sie eine Funktion erstellen, die nicht exportierte type internalString string Argumente akzeptiert, aber ich nehme an, das ist nicht das, was Sie vorschlagen, da Ihre CL Änderungen an den Parser und Typprüfung.)

@Merovius Gemäß dem Go-Vorschlagsprozess:

Konsens und Ablehnung

Ziel des Vorschlagsverfahrens ist es, zeitnah einen allgemeinen Konsens über das Ergebnis zu erzielen.

Wenn kein allgemeiner Konsens erreicht werden kann, entscheidet die Antragsprüfgruppe den nächsten Schritt, indem sie das Thema prüft und diskutiert und untereinander einen Konsens erzielt. Kann nicht einmal ein Konsens zwischen der Vorschlagsprüfgruppe erzielt werden (was äußerst ungewöhnlich wäre), überprüft der Schiedsrichter (rsc@) die Diskussion und entscheidet über den nächsten Schritt.

Beim Lesen der Kommentare schien der Konsens die Go-Code-Syntax zu bevorzugen. Wenn der Konsens jetzt tatsächlich darin besteht, bei //go:embed zu bleiben, respektiere ich das. Aber ich glaube nicht, dass der dokumentierte Prozess die anfängliche Entscheidung rechtfertigte, mit //go:embed .

(Im Moment bevorzugen die Umfrageergebnisse neue Funktionen schwach gegenüber neuen Direktiven, aber nicht viel. Wenn Daumen hoch nicht mindestens 2:1 überlegen sind, kann ich das einfach fallen lassen.)

@cespare

Es gibt keinen Präzedenzfall für diese neuen Arten von Funktionen, oder? Das heißt, etwas, das wie eine normale Paketfunktion aussieht, aber tatsächlich eine spezielle Art von Builtin ist, das nur auf eine bestimmte Weise aufgerufen werden kann?

Es gibt sowohl die vordeklarierten Funktionen des Universums als auch die Funktionen des unsicheren Pakets.

Sie können argumentieren, dass das unsichere Paket in der Go-Spezifikation dokumentiert ist, aber ich behaupte, dass dies nicht der Fall sein muss. Go unterstützte früher Modi, bei denen das Paket unsicher für Benutzer nicht verfügbar war, und auch heute hat Paket unsicher Funktionen und Einschränkungen, die nur in den Paketdokumenten dokumentiert sind, nicht in der Go-Spezifikation.

Es gibt auch interne Funktionen innerhalb der Go-Laufzeit, die nur vom Go-Compiler implementiert werden. ZB getg , getcallerpc , getcallersp und getclosureptr .

(Sie können die "nur Literalargumente zulässig" im normalen Go-Code annähern, indem Sie eine Funktion erstellen, die interne String-String-Argumente eines nicht exportierten Typs verwendet.

Ich denke, das wäre eine sinnvolle Ergänzung, um das Verhalten von Legacy-Typprüfungen weiter einzugrenzen.

aber ich nehme an, das ist nicht das, was Sie vorschlagen, da Ihre CL Änderungen am Parser und Typchecker vornimmt.)

CL 276835 ändert den Parser nicht, außer um den neuen Parser-Code zu entfernen, der für //go:embed hinzugefügt wurde. Es ändert zwar die Typprüfung, aber vergleichbar mit //go:embed zuvor.

Es wäre einfach, go/types zu erweitern, um die Einbettung von Paketen zu erkennen, aber ich habe mich dafür entschieden, CL 276835 nicht zu verwenden, um zu zeigen, dass es immer noch funktioniert (z.

@mdempsky Sie können anderer Meinung sein, ob zu diesem Zeitpunkt ein Konsens erzielt wurde oder nicht. Genauso wie Sie mit der Entscheidung selbst nicht einverstanden sein könnten. Ich glaube aber nicht, dass das wirklich etwas ändert. „Es gibt einen Konsens“ ist am Ende auch eine Entscheidung, die getroffen wurde. Und für diese Entscheidung gelten genau die gleichen Punkte, in denen es darum geht, für eine Stornierung neue Informationen zu benötigen.

Die Befriedigung jedes einzelnen Menschen ist ein DDoS-Vektor – sowohl was die Entscheidung angeht als auch für den Prozess, mit dem sie getroffen wurde.

FTR wurde die Frage nach Werkzeugen vs. Sprachänderung diskutiert , ebenso wie der Kompromiss zwischen "String-Literal vs. String-Konstante" ("String-Literal" erfordert Magie in der Typprüfung, "String-Konstante" erfordert die go-Tool zur Typprüfung - Kommentare brauchen beides nicht). Also auch hier gibt es nicht wirklich etwas Neues. Sie können mit dem Ergebnis dieser Entscheidung oder dem Verfahren, mit dem sie getroffen wurde, nicht einverstanden sein - aber die von Ihnen genannten Argumente wurden bei der Entscheidung berücksichtigt.

Die Befriedigung jedes einzelnen Menschen ist ein DDoS-Vektor – sowohl was die Entscheidung angeht als auch für den Prozess, mit dem sie getroffen wurde.

Ich verlange nicht, dass ich persönlich zufrieden bin und finde es beleidigend, dass Sie meine Beiträge als solche bezeichnen. Vorhin haben Sie auch mit mir geredet, als ob ich mit der Go-Sprache oder dem Go-Compiler nicht vertraut wäre. Bitte hör auf mit der Herablassung.

Ich habe oben zahlreiche Kommentare keine neuen //go: Direktiven hinzuzufügen, während niemand sie bejahend kommentiert hatte. Daher schien mir die überwältigende Präferenz der Community , die Go-Syntax zu bevorzugen, und mein Kommentar verteidigte ihre erklärten Präferenzen.

Wie es aussieht, hat https://github.com/golang/go/issues/41191#issuecomment -747095807 jedoch mehr Daumen nach unten als Daumen nach oben. Das überrascht mich, denn es scheint mit all den Kommentaren während der früheren Diskussion nicht vereinbar zu sein. Aber ich akzeptiere gerne, dass die Frage direkt angesprochen wurde, und (insbesondere als jemand, der an der langfristigen Unterstützung dieser Funktion innerhalb des Go-Compilers beteiligt sein wird) bin ich jetzt glücklicher, //go:embed sehen, dass dies tatsächlich die Präferenz der Gemeinschaft ist und nicht nur die Präferenz der Vorschlagsautoren. Dieses Ergebnis wäre nicht erreicht worden, wenn die Diskussion beendet worden wäre, wie Sie anscheinend beabsichtigt haben.

FTR wurde die Frage nach Werkzeugen vs. Sprachänderung diskutiert , ebenso wie der Kompromiss zwischen "String-Literal vs. String-Konstante" ("String-Literal" erfordert Magie in der Typprüfung, "String-Konstante" erfordert die go-Tool zur Typprüfung - Kommentare brauchen beides nicht).

Dieser Kommentar ist für meine Argumente irrelevant. Die alternative Schreibweise, die ich in CL 276835 als Prototyp erstellt habe, hat genau die gleichen technischen Eigenschaften wie die vorhandene //go:embed Schreibweise: Tools, die wissen müssen, welche Dateien eingebettet werden sollen, müssen aktualisiert werden, um Go-Quelldateien anders zu verarbeiten (z über den Missbrauch von //go:embed Kommentaren oder der eingebauten embed.Bytes Funktion), während bestehende Tools weiterhin vernünftig Code verarbeiten können, ohne sich um sie zu kümmern (zB go/types ignoriert //go:embed Kommentare erkennt aber nicht, ob es auf falsche Variablentypen angewendet wird, und kann embed.Bytes mithilfe der Stub-Deklarationen überprüfen, weiß aber nicht, ob alle Aufrufe abgelehnt werden sollen, die andere Argumente als String-Literale verwenden).

Die Frage, ob es sich bei beiden um einen "Sprachwechsel" handelt, ist eher philosophisch als technisch.

(Das Argument von Russ, dass "ob ein Programm gültig ist, ändert sich nicht" unter dem Vorschlag von //go:embed ist ebenfalls falsch. https://github.com/golang/go/issues/41191#issuecomment-747799509 gibt ein Beispiel für ein Paket, das gemäß der Go-Spezifikation gültig war und ist und auch von Go-Toolchain-Releases bis Go 1.15 akzeptiert wurde, aber in Go 1.16 nicht mehr gültig ist.)

Als jemand, der Matts Vorschlag, var website = embed.Files("logo.jpg", "static/*") , ist mein Anliegen bei der Verwendung des Kommentarformulars ( //go:embed logo.jpg static/* ) "Benutzerfreundlichkeit".

Zum Beispiel würden diese 2 Programmbeispiele 2 verschiedene Dinge ausgeben, nur weil ein "Import" verpasst wurde:

import (
    "fmt"
)

//go:embed sample.txt
var sample string

func main() {
    fmt.Println(sample) // will print a blank line
}
import (
    "embed"
    "fmt"
)

//go:embed sample.txt
var sample string

func main() {
    fmt.Println(sample) // will print contents of sample.txt
}

Indem Sie den Entwickler zwingen, den Import über die Sprachsemantik zu verwenden, minimieren Sie Probleme, bei denen die eingebetteten Dateien nicht wie erwartet funktionieren, weil sie nicht wie erwartet initialisiert werden.

@kushieda-minori Obwohl ich denke, dass mein Vorschlag auch einfacher zu verwenden ist, wird das erste Beispiel bereits bei tip kompiliert:

./x.go:7:3: //go:embed only allowed in Go files that import "embed"

Ich denke, dieses Problem wird auch durch #43217 weiter gemildert, da Sie "embed" importieren müssen, um Variablen vom Typ embed.Bytes und embed.String sowieso zu deklarieren. Und der Compiler (aber nicht go/types oder cmd/vet) meldet auch schon einen Fehler, wenn Sie die //go:embed Direktive auf eine Variable mit falschem Typ anwenden.

@mdempsky , jedoch wird das versehentliche Kompilieren auf einer älteren Version von Go nicht fehlschlagen und könnte den falschen Eindruck erwecken, dass die Einbettung funktioniert hat.

Ältere Versionen von Go haben keine Paketeinbettung, daher schlägt import "embed" fehl. Es ist wahr, dass Benutzer schreiben können:

package p

//go:embed foo.txt
var foo []byte

und es wird stillschweigend von Go 1.15 und älter akzeptiert. Aber es wird von Go 1.16 und neuer

Ich denke, eine geeignete (teilweise) Korrektur wäre, dass cmd/compile aufhört, unbekannte //go: Anweisungen zu akzeptieren. Eine weitere Einschränkung des aktuellen Vorschlags in Bezug auf die integrierte Syntax von Go besteht beispielsweise darin, dass Sie die Direktive //go:embed falsch eingeben (sagen wir //go:enbed , //go;embed oder // go:embed mit einem Leerzeichen), wird es auch stillschweigend ignoriert. (Während Tippfehler wie enbed.Bytes("foo.txt") selbst bei unveränderten go/types einen Typprüfungsfehler verursachen würden.)

Toller Punkt zu nicht validierten go: Direktiven. Wenn dies durchgesetzt würde, würde dies dazu beitragen, schwer zu erkennende Tippfehler zu vermeiden.

Ein weiterer Gedanke, den ich gerade hatte, ist, dass meine Tools so eingerichtet sind, dass Importe nach Bedarf automatisch hinzugefügt/entfernt werden. Wenn meine Werkzeuge veraltet sind, muss ich dann dagegen ankämpfen, den "unbenutzten" embed Import zu entfernen? Ich habe festgestellt, dass es gelöst ist, wenn ich embed.String usw. verwende, aber die Verwendung von regulären []byte] und string soll vollständig gültig sein. Dies könnte für einen neuen Gopher frustrierend sein, der Web-Snippets herauspickt, um etwas zum Laufen zu bringen, wenn er die spezifischen embed.* Aliasnamen nicht sieht.

aber die Verwendung von regulären []byte] und string soll vollständig gültig sein.

Sie werden nicht sein, wenn #43217 akzeptiert wird. Ich empfehle, sich über #43216 und #43217 zu informieren. Beide haben bisher eine überwältigend positive Unterstützung erfahren und scheinen mir sehr wahrscheinlich akzeptiert zu werden. (Ich bin jedoch nicht im Ausschuss für die Überprüfung von Vorschlägen.)

Danke, als ich #43217 das erste Mal durchgelesen habe, habe ich das Stichwort übersehen
"have" für die Verwendung der embed.* Typen.

Ich denke, meine einzige Sorge ist dann die letzte, auf die Sie hingewiesen haben.

Am Do, 17. Dez. 2020, 20:24 Uhr Matthew Dempsky [email protected]
schrieb:

aber die Verwendung von regulärem []byte] und string soll vollständig gültig sein.

Sie werden es nicht sein, wenn #43217 https://github.com/golang/go/issues/43217 ist
akzeptiert. Ich empfehle, auf #43216 nachzulesen
https://github.com/golang/go/issues/43216 und #43217
https://github.com/golang/go/issues/43217 . Sie haben beide erhalten
bisher überwältigend positive Unterstützung und scheint sehr wahrscheinlich akzeptiert zu werden
mir. (Ich bin jedoch nicht im Ausschuss für die Überprüfung von Vorschlägen.)


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/golang/go/issues/41191#issuecomment-747808153 oder
Abmelden
https://github.com/notifications/unsubscribe-auth/ADLZABJWBJX475BVYDVD6ODSVKVOTANCNFSM4QTHVTUA
.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen