Go: embed, cmd/go : ajouter la prise en charge des fichiers intégrés

Créé le 2 sept. 2020  ·  114Commentaires  ·  Source: golang/go

En juillet, @bradfitz et moi avons publié un projet de conception pour les fichiers intégrés . Le document renvoie à une vidéo, à un code prototype et à une discussion Reddit.

Les retours sur cette conception ont été extrêmement positifs.

Je propose d'adopter le projet de conception des fichiers embarqués pour Go 1.16, avec un ajout, suggéré dans la discussion, pour simplifier le cas de l'accès direct aux octets dans un seul fichier embarqué.

Tant qu'un fichier importe "embed" ( import _ "embed" si nécessaire), il sera permis d'utiliser //go:embed nommant un seul fichier (pas de modèles de glob ni de correspondance de répertoire autorisés) à initialiser une variable simple string ou []byte :

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

L'importation est nécessaire pour signaler le fichier comme contenant des lignes //go:embed et nécessitant un traitement. Goimports (et gopls, etc.) peuvent apprendre cette règle et ajouter automatiquement l'importation dans n'importe quel fichier avec un //go:embed si nécessaire.

La conception des fichiers intégrés dépend de la conception du projet d'interface du système de fichiers, que j'ai également proposé d'adopter dans #41190.

Ce problème concerne _uniquement_ l'adoption de la conception des fichiers intégrés , en supposant que la conception de l'interface du système de fichiers est également adoptée. Si cette proposition est acceptée avant la conception de l'interface du système de fichiers, nous attendrons simplement la conception de l'interface du système de fichiers avant de commencer à apporter des modifications.

Proposal Proposal-Accepted

Commentaire le plus utile

Aucun changement dans le consensus, donc accepté.

Tous les 114 commentaires

Serait-ce une erreur d'avoir une directive //go:embed sans importer embed ?

@jimmyfrasche Oui, avant-dernière liste à puces sur https://go.googlesource.com/proposal/+/master/design/draft-embed.md#go_embed -directives.

@rsc Peut-être que je l'ai raté dans le brouillon, mais je ne vois pas la possibilité d'intégrer un seul fichier que vous mentionnez dans votre commentaire.
Seriez-vous également en mesure d'intégrer un seul fichier en tant que chaîne const ?
Merci pour cette belle proposition.

@pierrec Ce n'est pas dans le projet de doc (le "un ajout" est le texte dans le commentaire ci-dessus). Les chaînes de const peuvent finir par jouer un rôle dans la décision de vérifier si un type de programme est vérifié, ce qui signifierait que tous les vérificateurs de type devraient comprendre //go:embed'ed consts. En revanche, si nous nous en tenons aux vars, les vérificateurs de types ne sont pas plus sages et peuvent être laissés seuls. On dirait que nous devrions probablement nous en tenir aux vars.

Y a-t-il une raison particulière pour laquelle vous vouliez un const au lieu d'un var ? Leur utilisation devrait être à peu près la même en ce qui concerne l'efficacité. (Les références aux chaînes const finissent par être compilées en ce qui concerne les références aux variables cachées de toute façon.)

Merci pour l'explication. J'ai tendance à intégrer des ressources statiques en tant que chaînes const pour le moment, c'est pourquoi j'ai demandé. Je suis bien avec vars aussi!

Intéressant, donc je pourrais faire quelque chose comme :

//go:embed version.txt
var Version string

Et potentiellement même avoir un commentaire //go:generate pour générer version.txt. Cela réduirait un grand cas d'utilisation pour makefiles/ldflags.

Est-ce une erreur si le fichier n'est pas trouvé ? Si oui, où techniquement l'erreur est-elle considérée comme se produisant ? L'heure du lien ?

Pouvons-nous nous assurer que go:embed s'exécute après go:generate afin que nous puissions faire des choses comme générer facilement des versions, etc. ?

Pouvons-nous nous assurer que go:embed s'exécute après go:generate afin que nous puissions faire des choses comme générer facilement des versions, etc. ?

D'après ma compréhension, go:generate se produirait avec go generate tandis que go:embed se produirait avec go build .

@carlmjohnson Oui, c'est toujours une erreur de dire //go:embed foo où foo n'existe pas.
L'erreur se produit lors de la compilation du fichier source contenant cette ligne.
(Si vous deviez supprimer foo après avoir compilé ce fichier source, vous n'arriveriez toujours pas à une étape de lien - la commande go remarquerait que le paquet doit être reconstruit car foo a été supprimé.)

Je pense que cette proposition n'est pas complète sans dire quelque chose sur ETag.
https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fzi0pok/

@tv42 , oui, nous ferons fonctionner ETag. Je ne sais pas quelle est la forme de cela, mais nous le ferons.
(Également affirmé sur https://github.com/golang/go/issues/35950#issuecomment-685845173.)

Deux Trois choses que j'ai remarquées en travaillant avec mjibson/esc :

  • Comme go:embed n'a pas besoin de générer des fichiers go pour l'intégration en tant que système de fichiers en lecture seule, cela éliminerait la peine de changer les horodatages sur les fichiers go:generate ed qui ont défié git porcelain tests sur CI- très sympa
  • Une chose que je n'ai pas trouvée dans la proposition mais dont j'aurais besoin est la possibilité de recharger en direct les fichiers intégrés pendant les cycles de développement. En utilisant mjibson/esc je peux actuellement le faire en lui demandant d'utiliser le système de fichiers local (bien qu'il ne récupère pas de nouveaux fichiers) et de modifier le comportement à l'aide de balises de construction. Je me demande ce que cela pourrait entrer dans la proposition?
  • Mettre à jour Une autre chose dont je me souviens est que esc nécessaire pour pouvoir supprimer de manière transparente (des parties de) le chemin de base afin, par exemple, d'exporter un dossier d'actifs en tant que racine Web.

Réflexion après coup : je suppose que le deuxième point pourrait être corrigé en conjonction avec la proposition io/fs où j'utiliserais soit le système de fichiers intégré ou le système de fichiers en direct pour l'inclusion ? Implémenter la suppression de chemin en tant que middleware io/fs ?

@andig Vous pouvez déjà préfixes lorsque vous servez un système de fichiers sur HTTP . J'accepte que le rechargement en direct puisse être effectué par une bibliothèque tierce enveloppant un io/fs .

Encore une chose : si j'ai bien compris, embed considérera les fichiers localement dans le package et interdit .. . Ma conception actuelle a /assets et /server/ où ce dernier contient le code du serveur et héberge aujourd'hui les fichiers générés. Avec cette proposition, l'intégration devrait être déplacée vers le dossier racine car les actifs ne seraient pas accessibles à partir du serveur. Cela impose des contraintes d'accessibilité différentes des importations normales. Je me demandais si cela est nécessaire pour des raisons de sécurité ou si les intégrations locales de module devraient être généralement autorisées.

Encore une chose : si j'ai bien compris, embed considérera les fichiers localement dans le package et interdit .. . Ma conception actuelle a /assets et /server/ où ce dernier contient le code du serveur et héberge aujourd'hui les fichiers générés. Avec cette proposition, l'intégration devrait être déplacée vers le dossier racine car les actifs ne seraient pas accessibles à partir du serveur. Cela impose des contraintes d'accessibilité différentes des importations normales. Je me demandais si cela est nécessaire pour des raisons de sécurité ou si les intégrations locales de module devraient être généralement autorisées.

Vous pouvez créer un fichier emed.go dans votre répertoire d'actifs et rendre les actifs disponibles en tant que son propre package pour le reste de votre programme.

Un autre objectif explicite est d'éviter un changement de langue. Pour nous, l'intégration d'actifs statiques semble être un problème d'outillage, pas un problème de langue.

D'accord. À mon avis, ajouter du sucre syntaxique dans le langage pour prendre en charge ce changement d'outil est un changement de langage. Je suis sûr que c'est évident pour les autres, mais c'est effectivement un commentaire en tant que code.

Je pense fortement que la magie/le sucre nuit à la simplicité et à la lisibilité de la langue ; il est très facile de rater un commentaire magique qui intègre un fichier. Bien qu'une réponse à cela puisse facilement être "d'accord, alors ne l'utilisez pas", ce changement signifie qu'un réviseur doit toujours être vigilant pour les autres utilisateurs de cette fonctionnalité et doit se rappeler que les commentaires autour des déclarations de variables peuvent casser les builds ou échouer à temps de compilation.

Je pense que cela va ajouter de la confusion, nuire à la convivialité du langage et entraîner des binaires opaques et volumineux sans avantage clair (en ce qui concerne la dernière préoccupation, cela conduira même à un anti-modèle de reconstruction des binaires en raison de modifications de fichiers simples ). Si go mod autorisait un --withNonGoCodeAssets , je pense que cela résoudrait les besoins de la plupart des développeurs qui ne veulent pas écrire des pipelines de construction plus complexes (je suppose que la distribution de l'utilisateur final est un sous-ensemble plus petit de le problème pour les utilisateurs).

@tristanfisher , je comprends votre point de vue sur le changement de langue par rapport à l'outillage. C'est certainement près de la ligne. La raison pour laquelle je considère qu'il s'agit davantage d'un changement d'outillage est que la spécification du langage n'est pas affectée - si un programme est valide ne change pas, le processus de vérification de type ne change pas. Tout ce qui change est la valeur initiale de cette variable après le commentaire. De cette façon, c'est un peu comme l'indicateur -X de l'éditeur de liens, qui peut définir la valeur initiale d'une var de niveau supérieur de type chaîne. C'est bien pour nous d'être en désaccord; Je voulais juste clarifier ma définition et expliquer la distinction que je fais.

En ce qui concerne le ballonnement, je suppose que nous devrons voir, mais je ne prévois pas que les programmes deviennent beaucoup plus gros qu'ils ne le sont déjà. Les gens exécutent _déjà_ des outils qui transforment des fichiers arbitraires en code Go, les archivent dans leurs dépôts et obligent le compilateur à les construire. La conception supprime certains frais généraux de ce processus mais ne permet rien de nouveau. Peut-être que les gens en abuseront maintenant que c'est plus facile à faire, mais dans l'ensemble, je ne m'attends pas à ce que ce soit un problème. (Et si une dépendance intègre quelque chose de si gros qu'il gonfle vos binaires, vous pouvez toujours choisir de ne pas utiliser cette dépendance.)

En ce qui concerne les reconstructions dues à des modifications de fichiers simples, les seuls fichiers pouvant déclencher des reconstructions sont ceux de votre propre module de niveau supérieur, car les dépendances sont immuables. Si vous constatez que des reconstructions se produisent plus souvent que vous ne le souhaiteriez, la seule explication est (1) vous intégrez des fichiers et (2) vous modifiez ces fichiers. Vous auriez le contrôle total de faire quelque chose pour l'une ou l'autre cause. (Ce serait une tout autre chose si le choix d'une dépendance de ce qu'il fallait utiliser vous imposait en quelque sorte des reconstructions supplémentaires ou d'autres dépenses. Mais ce n'est pas le cas ici.)

@rsc Je suis d'accord pour dire que nous ne sommes pas d'accord et j'apprécie votre réponse. Mon sentiment est que si c'est inclus par défaut dans l'outillage standard et que les commentaires peuvent conduire à une initialisation implicite d'une variable, alors c'est un changement de langage. En dehors de ce débat, je suppose que mon sentiment dégoûtant concerne davantage les directives que les commentaires "magiques" qui doivent être mémorisés par les lecteurs de code (humains). Cela pourrait être amené à la conclusion absurde d'ajouter de nouvelles fonctionnalités via des commentaires de bloc qui sont gérés au moment de la construction.

Cela dit, si cela est ajouté à l'écosystème, je serai reconnaissant que l'importation de embed soit requise - c'est mieux que rien en tant que "hey, head's up" lors de l'audit de code. Je pense que go mod autoriser non .go résoudrait la majorité des cas d'utilisation (j'imagine que la plupart des gens vont créer des fichiers pour les serveurs Web) et vivraient également entièrement dans l'outillage.

Je pense que votre point concernant l'éditeur de liens est bon. Cela aide également à expliquer mes sentiments à ce sujet : si l'utilisateur final (par exemple, pas quelqu'un qui importe simplement un package) prend la décision, il n'y a aucun moyen d'être surpris par des gouttes de non-code qui arrivent pour le trajet. Mes inquiétudes sont nées de la révision/du jumelage du travail des autres et des responsabilités « technologiques », c'est pourquoi j'ai ressenti le besoin de répondre.

Je pense que "nous devrons voir" résume bien (je suis plus cynique à propos des ballonnements/abus).

Je vais lire le projet de conception ce soir, jusqu'à présent, il semble bon du point de vue de TinyGo.

Je voulais juste préciser une chose :

D'autre part, des projets comme TinyGo et les systèmes cibles U-root avec plus de RAM que de disque ou de flash. Pour ces projets, la compression des actifs et l'utilisation d'une décompression incrémentielle au moment de l'exécution pourraient permettre de réaliser des économies importantes.

Je ne sais pas pour U-root, mais pour TinyGo, les principales cibles sont les microcontrôleurs qui ont normalement beaucoup plus de flash que de RAM (généralement un facteur de 8 ou 16). Un rapide coup d'œil au projet de conception semble suggérer que l'idée est de conserver les fichiers en mémoire morte, ce qui fonctionnerait bien pour ces cibles : les fichiers intégrés peuvent être lus directement à partir de la mémoire flash. Il ne serait probablement pas souhaitable que les cibles TinyGo décompressent les fichiers lors de l'exécution.

La proposition io/fs dont cela dépend semble être bloquée sur des problèmes de Readdir/FileInfo, en cours de discussion dans #41188 et précédemment #40352.

J'ai rédigé une API pour les remplacer dans https://github.com/golang/go/issues/41188#issuecomment -686283661

@andig

Une chose que je n'ai pas trouvée dans la proposition mais dont j'aurais besoin est la possibilité de recharger en direct les fichiers intégrés pendant les cycles de développement.

embed.Files implémente fs.FS, donc tout ce que vous avez à faire est d'utiliser la balise de construction dev vs !dev pour basculer une variable entre embed.Files et le vrai FS.

J'ai déposé #41265. Il propose une nouvelle API ReadDir() pour io/fs.

J'ai les mêmes préoccupations que //go:embed est susceptible d'être plus courant. Peut-être est-il temps d'envisager une syntaxe différente pour les directives du compilateur ?

Juste un rappel que changer la syntaxe Go a un coût très élevé. Pratiquement tous les outils Go devraient être mis à jour et/ou corrigés pour prendre en charge la nouvelle syntaxe, par exemple.

Je ne les considère pas comme des commentaires magiques. Les lignes commençant par //go: sont des directives et peuvent être définies comme telles dans la spécification. Il n'y a pas beaucoup de différence sémantique entre //go:embed , @embed , [[embed]] ou tout autre nombre de variations de syntaxe, sauf que le préfixe //go: est déjà traité comme non -code par les outils Go. (mon éditeur met en évidence ces lignes différemment par exemple)

@mvdan Si cette proposition se produit, la syntaxe de Go a changé. C'est juste changé d'une manière qui ne casse pas l'outillage existant. Peut-être que cela semble pédant.

@iand je ne suis pas pointilleux sur la syntaxe spécifique des directives du compilateur. Je pense juste que cela doit être formalisé à un moment donné et les règles spécifiées.

Je pense que cette proposition est une bonne idée. Il résout un problème courant. Mon souci est que le coût de son adoption soit rendu un peu plus explicite.

@jonbodner Je partage vos inquiétudes concernant les commentaires magiques. Mais dans une certaine mesure, les règles sont spécifiées par #37974.

@networkimprov , ce n'est pas la proposition io/fs. S'il vous plaît arrêtez de commenter sur ReadDir ici.

@jonbodner

Je ne suis pas pointilleux sur la syntaxe spécifique pour les directives du compilateur. Je pense juste que cela doit être formalisé à un moment donné et les règles spécifiées.

Je voudrais juste souligner que nous avons pris la décision d'utiliser //go: pour marquer les directives de la chaîne d'outils Go lorsque
nous avons ajouté (l'utilisation limitée) l' annotation
Nous avons ajouté //go:noescape pour les auteurs d'assembly en 2013.
Nous avons ajouté //go:generate en 2014.
Nous ajoutons probablement //go:build en 2020-2021 également.
Il y en a d'autres ; ce ne sont que les points saillants.
Vous pouvez penser à //go: comme signifiant #pragma de C si cela vous aide.

À ce stade, la convention est très bien établie.
Nous avons choisi cette syntaxe en 2012 parce que
(1) ce n'est évidemment pas un commentaire pour une personne ;
(2) les outils qui ne connaissent pas les commentaires les ignoreront car ce sont des commentaires ; et
(3) il se généralise à d'autres outils (s/go/yourtool/).

Et comme Ian l'a dit, #37974 a formalisé la syntaxe exacte des commentaires généralisés, pour ce que ça vaut.

Sur la base de la discussion ci-dessus, cela semble être une acceptation probable .
(Encore une fois, en supposant mais distinct de la proposition FS.)

Aucun changement dans le consensus, donc accepté.

Je suis impatient de mettre la main sur l'intégration. Cela peut-il déjà être testé sur le maître ou est-il prévu de l'expédier en tant qu'expérience au cours du cycle 1.15 ?

@andig , Go 1.15 est déjà sorti. J'espère toujours que ce sera dans Go 1.16 et atterrira dans la branche de développement ce mois-ci.

@rsc 1.16 disponible ?

@septs , non, nous travaillons toujours sur Go 1.16. Le gel du code est le 31 octobre, avec une date de sortie cible le 1er février.

la version la plus rapide du premier trimestre ou du deuxième trimestre ?

@septs s'il vous plaît arrêtez de poser des questions sur les versions Go dans ce fil. Plus d'une vingtaine de personnes la suivent et sont notifiées. Voir https://golang.org/wiki/Questions et https://github.com/golang/go/wiki/Go-Release-Cycle.

Le changement https://golang.org/cl/243941 mentionne ce problème : go/build: recognize and report //go:embed lines

Le changement https://golang.org/cl/243940 mentionne ce problème : go/build: refactor per-file info & reader

Le changement https://golang.org/cl/243942 mentionne ce problème : embed: implement Files

Le changement https://golang.org/cl/243944 mentionne ce problème : cmd/compile: add //go:embed support

Le changement https://golang.org/cl/243945 mentionne ce problème : cmd/go: add //go:embed support

Un détail qui est ressorti de l'examen de la mise en œuvre est que "Files" en tant que nom singulier est assez gênant ("A Files hold ...").

Le choix de embed.Files pour le nom est antérieur à la fois à la proposition io/fs et à la prise en charge des chaînes et des []octets.
Compte tenu de ces deux développements, une façon apparemment sensée de résoudre le problème « Un fichier est bloqué » consiste à l'appeler un FS au lieu d'un fichier.

Ensuite, les trois façons d'intégrer et d'imprimer des données sont :

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

Cela semble plus clair sur ce que vous obtenez : une chaîne, un [] octet ou un FS.
C'est-à-dire que la plupart des fonctionnalités de embed.F* viennent du fait qu'il s'agit d'un fs.FS, et l'appeler FS rend cela plus clair que de l'appeler Files.

J'ai apporté ce changement dans ma dernière version du package d'implémentation CL, mais je voulais revenir ici et voir s'il y a des objections au changement de nom.

(Un changement plus radical serait de faire var f fs.FS au lieu de var f embed.FS , mais cela empêcherait d'avoir une méthode sur f autre que Open. Par exemple, ci-dessus, avoir ReadFile est pratique et ne serait pas possible. Et en général, nous avons appris que l'utilisation d'un type concret pour quelque chose qui pourrait vouloir ajouter des méthodes plus tard est une bonne pérennité par rapport à l'utilisation directe d'un type d'interface.)

Je pense que le changement de nom est un bon changement.

Concernant le changement plus radical :

  • Si nous utilisions fs.FS , aurions-nous encore besoin du package embed ? Je suppose que la valeur dynamique doit toujours avoir un type, qui vit dans un package? Je trouve l'idée de ne pas avoir à ajouter de package un plus.
  • Je ne trouve pas "nous ne pouvons pas ajouter de méthodes" super convaincant, car IMO f.ReadFile(…) n'est pas beaucoup moins pratique que fs.ReadFile(f, …) .
  • Je suis d'accord que les types concrets sont meilleurs en général, donc c'est un plus pour le garder embed.FS
  • Autre question : embed.FS utilise-t-il des récepteurs de pointeur ou des récepteurs de valeur ? L'OMI doit passer autour de &f est gênant, l'utilisation de récepteurs de valeur est légèrement inattendue. Nous pouvons également autoriser var f *embed.FS . Si la variable a un type d'interface, cette question disparaît.

Dans l'ensemble, je suis toujours d'accord qu'utiliser le concret embed.FS est mieux - si rien d'autre, alors à des fins de documentation.

Maintenant que vous l'avez mentionné, je ne pense pas avoir été clair : nous pouvons intégrer des répertoires, n'est-ce pas ?

Oui en tant qu'embed.FS qui implémente fs.FS.

@Merovius , embed.FS utilise des récepteurs de valeur. embed.FS est une structure d'un mot contenant un seul pointeur, il n'y a donc pas de surcharge réelle pour le faire, mais cela signifie que vous pouvez les affecter et les utiliser sans vous soucier des *s et \&s partout.

@chabad360 , oui, vous pouvez intégrer des répertoires.

Et les liens symboliques ?

@burik666 , voir https://golang.org/s/draft-embed-design pour plus de détails, mais non, vous ne pouvez pas intégrer un lien symbolique.

sera-t-il possible d'intégrer et d'utiliser des bibliothèques C dynamiques ? si oui, comment utiliserions-nous le chemin d'intégration dans les en-têtes #cgo tels que : #cgo LDFLAGS: -L./lib -lmylib -Wl,-rpath=./lib ?

@benitogf Je suppose que la seule vraie façon de le faire serait de les écrire sur le disque et d'utiliser dlopen . Je ne peux pas imaginer comment vous pourriez dire au chargeur dynamique comment trouver les fichiers intégrés. De plus, si vous souhaitez regrouper en code C, la liaison statique semblerait de toute façon plus appropriée, non ?

@benitogf Embedding vous permet de mettre
Si vous avez un moyen d'utiliser une bibliothèque C dynamique qui est déjà dans votre programme sous la forme d'un []octet, alors l'incorporation vous aidera à y obtenir un fichier disque. Sinon, non.

un lien statique semblerait plus approprié de toute façon, non?

@Merovius a accepté, mais j'ai plusieurs cas d'utilisation travaillant avec des fournisseurs qui ne fourniront que des bibliothèques dynamiques

Si vous avez un moyen d'utiliser une bibliothèque C dynamique qui est déjà dans votre programme sous la forme d'un [] octet
le seul moyen réel de le faire serait de les écrire sur le disque et d'utiliser dlopen

écrire la bibliothèque embarquée à partir de []byte vers le système de fichiers et utiliser dlopen semble correct, bien que le fait d'avoir les fichiers embarqués éventuellement "vidés" sur le système de fichiers lors de la construction/exécution afin que l'en-tête #cgo puisse y accéder serait utile, pas seulement pour cgo à mon humble avis

Essayez ceci maintenant ; un problème avec la directive go:embed est que si j'intègre build/* , les noms de fichiers ont toujours le préfixe build/ . Si je veux ensuite servir ce répertoire via http.FS , il n'y a pas de moyen simple d'_ajouter_ le préfixe requis pour y accéder si nécessaire (sans écrire un wrapper, ce qui pose alors le problème de devoir répertorier toutes les méthodes potentielles que le FS peut avoir...).

par exemple:

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

Je sais que l'intention est que l'on puisse écrire go:embed foo/* bar/* baz.ext et obtenir tous ces fichiers, mais je pense qu'il sera très courant d'intégrer simplement un répertoire et de le servir en tant qu'actifs statiques via le package http. Je m'attends à ce que ce soit un piège alors que les gens passent de choses comme http.Dir("static") ou pkger.Dir("/internal/web/static") où le préfixe est déjà géré, au nouveau embed.FS .

Je ne sais pas vraiment comment classer cela, car c'est une sorte d'interaction avec embed , io/fs , et net/http .

@zikaeroh Écrire un http.Handler fonctionnerait également là-bas, non ? Ce n'est jamais qu'une méthode et il y a même http.HandlerFunc . Peut-être que la bibliothèque standard pourrait même en fournir un pour mettre en miroir http.StripPrefix (quelque chose comme http.AddPrefix ou http.ReplacePrefix ).

Potentiellement, bien que cela semble un peu étrange de modifier la requête HTTP pour contourner l'implémentation FS (par opposition à un généralisé "donnez-moi un FS qui est un sous-répertoire d'un autre FS", ce qui n'est pas simple avec des méthodes optionnelles). Ce ne serait pas la chose la plus efficace, supprimer puis ajouter à nouveau un autre préfixe (étant donné les http.Request copies), mais je ferai un essai plus tard. Ce n'est au moins pas _différent_ du schéma actuel des choses où vous devez travailler avec la demande, je suppose.

J'ai quelques autres endroits où j'utilise des données statiques non via le package http pour lesquels je devrai trouver un correctif similaire.

Si je dois jeter un œil à la façon dont il est mis en œuvre, où puis-je regarder. Une branche où il est mis en œuvre ?

Il a été suggéré auparavant d'intégrer les fichiers sur place, c'est-à-dire de le faire dans le répertoire de construction, puis de l'importer. Cela supprimerait le préfixe de construction. Utilisez ensuite le gestionnaire pour ajouter le préfixe requis. Je ne sais pas comment exclure le fichier go effectuant l'intégration de l'intégration elle-même. Voir https://github.com/golang/go/issues/41191#issuecomment-686621090

Il a été suggéré auparavant d'intégrer les fichiers sur place, c'est-à-dire de le faire dans le répertoire de construction, puis de l'importer. Cela supprimerait le préfixe de construction. Utilisez ensuite le gestionnaire pour ajouter le préfixe requis. Je ne sais pas comment exclure le fichier go effectuant l'intégration de l'intégration elle-même. Voir #41191 (commentaire)

Ce n'est malheureusement pas bon pour les répertoires produits par d'autres outils, par exemple la sortie d'un webpack build ou CRA (où ils sont souvent nettoyés au préalable et non archivés). Je préfère pirater les noms de fichiers.

Il a été suggéré auparavant d'intégrer les fichiers sur place, c'est-à-dire de le faire dans le répertoire de construction, puis de l'importer. Cela supprimerait le préfixe de construction. Utilisez ensuite le gestionnaire pour ajouter le préfixe requis. Je ne sais pas comment exclure le fichier go effectuant l'intégration de l'intégration elle-même. Voir #41191 (commentaire)

Ce n'est malheureusement pas bon pour les répertoires produits par d'autres outils, par exemple la sortie d'un webpack build ou CRA (où ils sont souvent nettoyés au préalable et non archivés). Je préfère pirater les noms de fichiers.

Si vous utilisez un système de plugins aussi énorme que webpack, il est facile d'installer simplement un autre plugin webpack pour générer le fichier embed.go avec les actifs eux-mêmes. Si vous utilisez quelque chose de plus simple avec un makefile ou un script shell, il est également facile de générer le fichier .go à partir de là.

@zikaeroh

par opposition à un "donnez-moi un FS qui est un sous-répertoire d'un autre FS" généralisé, ce qui n'est pas simple avec des méthodes optionnelles

C'est censé être simple. La gestion du problème d'emballage faisait partie du processus de conception. En particulier, le wrapper est censé implémenter toutes les méthodes optionnelles, en appelant simplement la fonction d'assistance appropriée dans fs . Si cela ne fonctionne pas, c'est préoccupant et ce serait bien d'avoir des détails.

De plus, IMO, une implémentation d'un tel wrapper (qui supprime un préfixe) devrait finalement être fournie par io/fs (analogue à io.LimitWriter , etc.). Je suppose que la seule raison pour laquelle cela ne s'est pas encore produit est le temps.

@andig Le problème avec cela est que le fichier Go contenant la directive d'

Le problème avec cela est que le fichier Go contenant la directive d'intégration et la variable est alors également visible depuis le FS (et serait servi par HTTP ou pourrait être exposé d'une autre manière).

Une façon de remédier à cela pourrait être d'ajouter la possibilité d'exclure des fichiers/dossiers spécifiques de l'intégration ( @rsc ?)

Une façon de remédier à cela pourrait être d'ajouter la possibilité d'exclure des fichiers/dossiers spécifiques de l'intégration ( @rsc ?)

La proposition a été acceptée il y a plus d'un mois et est déjà mise en œuvre ; Je ne pense pas que des changements de conception importants comme la possibilité d'exclure des chemins soient raisonnables à ce stade. Si vous avez un problème avec la conception implémentée que vous ne pouvez pas contourner, je vous suggère de remplir un rapport de bogue séparé avec des détails, qui peuvent ensuite être suivis avant la version finale 1.16.

@Merovius

C'est censé être simple. La gestion du problème d'emballage faisait partie du processus de conception. En particulier, le wrapper est censé implémenter toutes les méthodes optionnelles, en appelant simplement la fonction d'assistance appropriée dans fs. Si cela ne fonctionne pas, c'est préoccupant et ce serait bien d'avoir des détails.

Comment fonctionnerait la suppression de préfixe pour Glob ?

@icholy je suppose quelque chose comme

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
}

Peut-être avec quelques précautions supplémentaires.

En jouant avec ça sur gotip, je remarque qu'il inclura des fichiers .DS_Store. C'est à peu près inévitable, je suppose, mais je crains que l'inclusion de fichiers dot entraîne une inclusion accidentelle de fichiers. Peut-être que les docs devraient avoir un avertissement fort à ce sujet?

Mon shell n'inclut pas les fichiers de points dans * , donc si je veux les inclure, je dois utiliser * .* . Cela pourrait être un moyen (peut-être tout aussi surprenant) de donner un niveau de contrôle.

Je ne sais pas quoi penser - IMO, les fichiers dot ne devraient pas vraiment être traités différemment par le modèle, mais OTOH l'exemple .DS_Store semble être quelque chose qui devrait vraiment être abordé.

Pourquoi ne pas simplement faire git clean -dffx && go build ? Si les fichiers DS_Store sont dans git, ils seront alors inclus dans le build. S'ils ne le sont pas, ils ne le seront pas. Cela fonctionnera également très bien avec gitignore.

De toute façon, vous devriez construire avec une caisse VCS propre. Si vous ajoutez des fichiers temporaires aléatoires, ils peuvent être intégrés à la version finale et vous ne pouvez pas savoir avec quels fichiers les gens se retrouveront. Nous pourrions vouloir documenter ceci.

@mvdan Je suis d'accord sur le principe, mais dans la pratique, je crains que beaucoup de gens ne soient brûlés par des builds sales s'ils ne sont pas avertis. Je ne veux pas voir une fuite secrète que quelqu'un n'a pas réalisé que son fichier .env a été intégré par erreur. J'ai vu des tonnes d'exemples d'erreur équivalente avec l'hébergement PHP, même si cela aurait pu être facilement évité en disant à Apache d'exclure les fichiers dot.


Re : intégration de http.FileServers

Si vous utilisez un système de plugins aussi énorme que webpack, il est facile d'installer simplement un autre plugin webpack pour générer le fichier embed.go avec les actifs eux-mêmes.

C'est vrai, mais c'est très compliqué. Le but d'embed.FS est de réduire le besoin de contournements fous de Makefile. Je pense que la solution simple est d'avoir fs.WithPrefix(string) fs.FS qui verrouille un FS dans un sous-répertoire. Je pensais qu'il y avait une discussion à ce sujet dans la proposition, mais je ne la trouve pas maintenant. Peut-être que c'était juste évoqué sur Reddit ou quelque chose.

Une façon de remédier à cela pourrait être d'ajouter la possibilité d'exclure des fichiers/dossiers spécifiques de l'intégration ( @rsc ?)

Cela pourrait être quelque chose comme

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

La directive embed-exclude pourrait simplement faire un filtre global sur les fichiers acceptés et supprimer toutes les correspondances…

Si nous voulons éviter d'ajouter plus à cette proposition, il pourrait également s'agir d'une règle de lint qui vérifie les systèmes de fichiers intégrés pour les fichiers dotfiles potentiellement inattendus et vous avertit afin que vous puissiez corriger votre build pour les supprimer.

Ou excluez les fichiers dot par défaut à moins qu'ils ne soient spécifiquement mentionnés en ajoutant .* ou similaire.

Cela ne prendrait toujours pas soin d'exposer un fichier assets.go. Quant à la proposition déjà mise en œuvre, sachez que la question de la génération d'actifs a été soulevée lors de la phase de discussion. Il n'est probablement pas dangereux d'exposer un asset.go (sinon vide, à l'exception de la directive embed), mais ce serait plus propre de ne pas l'avoir. Comme d'habitude, toutes sortes de solutions de contournement peuvent être appliquées.

Il n'est probablement pas dangereux d'exposer un asset.go (sinon vide, à l'exception de la directive embed), mais ce serait plus propre de ne pas l'avoir.

Je suis d'accord qu'il est très peu probable que ce soit un problème, mais je détesterais voir un code source fermé divulgué accidentellement en raison d'une mauvaise configuration si nous pouvons faciliter la configuration correcte des choses.

Je ne veux pas voir une fuite secrète que quelqu'un n'a pas réalisé que son fichier .env a été intégré par erreur.

Si quelqu'un utilise //go:embed static/* et qu'il y a un static/.env ou static/.super-secret , ne diriez-vous pas que l'utilisateur voulait vraiment inclure ces fichiers ? Sinon, pourquoi seraient-ils dans le répertoire statique ?

Je comprends que cela correspond à ce que l'utilisateur attend et à ce que * signifie dans la plupart des contextes, mais je pense personnellement que https://golang.org/pkg/path/filepath/#Glob sémantique est notre seul bien option. C'est le plus simple et ce à quoi la plupart des utilisateurs de Go seront habitués dans le cadre du développement de Go.

Je pense que mettre en garde contre les dangers de l'intégration de * est une bonne idée dans tous les cas, car dans un nombre important de cas, on pourrait réduire les risques d'erreur en utilisant des globs plus spécifiques comme *.png .

De plus, je vous encourage toujours à déposer un problème séparé qui peut être suivi par rapport à la version 1.16, écrit du point de vue d'un rapport de bogue. Cette proposition est acceptée et mise en œuvre, j'imagine donc qu'elle sera fermée très bientôt. Par exemple, vous pouvez formuler le rapport de bogue comme : la prise en charge des fichiers intégrés conduit facilement à l'inclusion de fichiers indésirables (et donnez quelques exemples).

Si quelqu'un utilise //go:embed static/* et qu'il y a un static/.env ou static/.super-secret, ne diriez-vous pas que l'utilisateur voulait vraiment inclure ces fichiers ? Sinon, pourquoi seraient-ils dans le répertoire statique ?

Il y a une énorme quantité de cas de coin, par exemple vous avez ouvert une session d'édition avec vim, mais ne l'avez pas fermée, et cela a créé .*.swp fichier

Déplacement de la discussion au #42321.

Cela serait très apprécié par l'équipe Prisma pour notre client Database Go , car nous utilisons un moteur de requête au moment de l'exécution qui est écrit en rouille et doit être dans le binaire go construit d'une manière ou d'une autre.

La façon dont nous procédons actuellement consiste à compresser le binaire dans un fichier .go au moment de go:generate, mais la taille du fichier est beaucoup plus élevée que lorsqu'il s'agit d'un fichier binaire .gz.

Les intégrations natives rendraient cela bien meilleur afin que nous puissions incorporer directement un fichier .gz dans le binaire go final.

Si quelqu'un utilise //go:embed static/* et qu'il y a un static/.env ou static/.super-secret , ne diriez-vous pas que l'utilisateur voulait vraiment inclure ces fichiers ?

Non, je ne le ferais pas.

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

Voir mon commentaire ultérieur dans https://github.com/golang/go/issues/42328#issuecomment -720169922.

J'adore l'idée de rendre les fichiers / modèles statiques intégrables, ce qui peut certainement augmenter considérablement la productivité des développeurs.

Mais devrions-nous innover une autre balise (par exemple @ ou autre) autrement que pour réutiliser ce // , qui est censé être des commentaires ?

À partir de maintenant, le // a été galvaudé, je suppose, pensez à ceux-ci :

// +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
...

Mais devrions-nous innover une autre balise (par exemple @ ou autre) autrement que pour réutiliser ce // , qui est censé être des commentaires ?

C'est une discussion séparée et bien qu'il serait bien que les directives ne soient pas des commentaires, une grande partie du code de compatibilité go1 repose déjà sur cela, il est donc probable que cela ne changera pas.

Mais devrions-nous innover une autre balise (par exemple @ ou autre) autrement que pour réutiliser ce // , qui est censé être des commentaires ?

J'ai essayé de le trouver et j'ai échoué, mais je me souviens qu'il y avait eu une décision de standardiser sur //go:<word> pour ce genre de directives.
Cela ne semble pas être une bonne idée de changer la syntaxe, étant donné la décision de converger expressément vers eux.
(De plus, bien sûr, ce sont des commentaires spécifiquement pour que le compilateur les ignore - ces directives sont spécifiques à l'outil go, elles ne devraient donc pas fuir dans le langage proprement dit)

J'ai vu une mention sur le problème io/fs que embed.FS prend en charge ETag : https://github.com/golang/go/issues/41190#issuecomment -737702433

J'ai essayé de faire un test pour cela, mais je ne vois pas d'en-tête de réponse ETag . Peut-être que je comprends mal l'utilisation. Dois-je m'attendre à en voir un ici ? https://play.golang.org/p/Wq5xU5blLUe

Je ne pense pas. http.ServeContent (utilisé par http.FileServer ) inspecte l'en-tête ETag, mais ne le définit pas, AIUI.

Dans un commentaire ci-dessus, Russ a déclaré qu'ETag fonctionnerait . La difficulté est de savoir comment faire en sorte que embed.FS communique à http.FileServer les informations nécessaires pour définir ETag ou d'autres en-têtes de mise en cache. Il devrait probablement y avoir un problème de suivi distinct pour cela.

Personnellement, je dirais que embed.FS devrait utiliser l'heure du dernier commit du module concerné comme ModTime . Cela correspondrait à peu près à debug.BuildInfo , donc cela n'aurait pas d'impact sur la reproductibilité. Bien que je ne sache pas comment cela est défini pour les commits qui ne correspondent pas à une version étiquetée et cela laisserait toujours la question de savoir à quoi le définir pour les builds à partir d'arbres de travail sales.

Mais j'espère que @rsc a une bonne solution en tête :)

Je ne suis pas sûr de comprendre le commentaire sur le « temps d'engagement » ; si la source du module est un fichier zip, il n'y a pas de "commit". Je ne vois aucun moment mentionné dans debug.BuildInfo ou debug.Module .

Plus important encore, je dirais que tout mécanisme basé sur l'horodatage est strictement inférieur à un etag approprié (basé sur le hachage du contenu).

@tv42 Chaque version de module est soit a) une version sémantique dérivée d'une balise (qui pointe vers un commit) ou b) une pseudo-version contenant un commit-hash. Je pense? Au moins dans git. J'ai peut-être mal compris quelque chose.

Plus important encore, je dirais que tout mécanisme basé sur l'horodatage est strictement inférieur à un etag approprié (basé sur le hachage du contenu).

Je ne suis pas si sûr. Soit il a besoin d'un canal secondaire pour communiquer le hachage, soit le serveur doit calculer le hachage du fichier sur demande (ce qui semble assez coûteux). Après tout, net/http ne sait pas, a priori, si le contenu de fs.FS peut changer. Le résultat final d'un ETag basé sur le hachage pourrait justifier le coût de l'ajout d'un tel canal secondaire (comme une interface optionnelle), mais cela ne semble évidemment pas strictement meilleur.

De plus, je dirais que le soutien au moins aussi une approche basée sur le temps voudrait dire que vous pouvez travailler avec plus de clients. Je n'ai pas de données pour soutenir cela, cependant (c'est-à-dire que je ne sais pas s'il existe et combien de clients peuvent prendre en charge If-Modified-Since mais pas ETag et vice-versa).

Mais vraiment, je me fiche de l'approche choisie. Je voulais juste mentionner la possibilité d'utiliser l'heure à laquelle une version de module a été balisée.

Les modules

$ 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

Il semble y avoir un horodatage dans le fichier *.info l'accompagne, mais je ne sais pas si c'est fiable ou disponible pour l'outillage.

$ 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"}

Même dans ce cas, quel horodatage doit être utilisé : incorporer l'utilisation dans le module principal (celui dans lequel vous exécutez go build ) ?

Personnellement, dans le contexte des fichiers statiques qui sont immuablement intégrés dans le binaire, le hachage semble juste supérieur aux horodatages à tous égards (à l'exception de la prise en charge héritée d'avant l'existence d'ETag, pré-HTTP/1.1, les années 90), et n'a aucune source possible de confusion dans les systèmes distribués et les versions concurrentes non plus. Je doute sérieusement des clients qui ne comprennent plus l'existence d'ETag, et ils ne feront certainement pas la transition HTTP/2.

Un canal latéral pour communiquer le hachage me semble être la bonne chose ; une interface FS facultative pour renvoyer un hachage du fichier. (Et peut-être un autre pour demander un algorithme de hachage spécifique, s'ils l'ont ?. Certains cas d'utilisation de service ont vraiment besoin de sha256/sha1/md5, pas seulement de "un peu de hachage"). Un FS qui n'a pas de réponse suffisamment bon marché peut choisir de ne pas l'implémenter.

(Bien que les hachages modernes soient des gigaoctets/seconde/cœur même lorsqu'ils sont cryptographiquement sécurisés, des dizaines de gigaoctets/seconde pour les moins sécurisés, et faciles à mettre en cache en fonction des appels de statistiques. Il suffit de prendre en charge ETag partout, les gens.)

J'ai réfléchi à cette proposition aujourd'hui. Je suis heureux de voir cette fonctionnalité incluse dans la chaîne d'outils Go, et j'apprécie toute la réflexion et les efforts qui ont été consacrés à la proposition à ce jour. Merci d'avoir proposé cela et de faire avancer cela.

J'ai deux questions, puis une suggestion (appréciant que la période de discussion des propositions est dépassée et que le gel des versions est déjà en cours) :

Types autorisés

Je remarque que le code actuel s'attend à ce que les variables soient déclarées avec précision ~ embed.FS ~, string , ou []byte . En particulier, ceux-ci ne sont pas autorisés : []uint8 , ~ FS après l'importation de points de "embed"~, ou tout autre type identique construit à l'aide d'alias de type. (Edit : j'ai oublié comment les importations de points fonctionnent dans gc et j'ai mal lu le code pour détecter embed.FS .)

Est-ce voulu ou un bug ?

Sémantique des variables de type []byte .

Je ne vois aucune mention de la sémantique d'identité attendue des variables de type []byte . En particulier, pour les variables à portée de fonction. Ce n'est pas important pour les variables de type string - et embed.FS -, car elles font référence à des données immuables qui peuvent être dédupliquées en toute sécurité. Mais il est important de connaître la sémantique prévue pour les variables de type []byte .

Avec l'implémentation actuelle, le programme de test ci-dessous affiche false true (lorsque foo.txt n'est pas vide). Est-ce voulu/garanti ?

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

Je pense que la sémantique la plus proche de Go pour les variables //go:embed serait celle des littéraux composites : chaque exécution génère une nouvelle copie.

S'il n'y a pas de consensus sur la sémantique appropriée pour cela, nous pouvons toujours l'utiliser et créer une portée de fonction, []byte -typed intègre une erreur pour Go 1.16 : les utilisateurs peuvent toujours déclarer une variable au niveau du package s'ils veulent la sémantique actuelle (une tranche d'octet par déclaration de source), ou utilisent une variable de type string et convertissent en []byte s'ils veulent une sémantique littérale composite. Nous pourrions ensuite revoir plus tard le comportement dont les utilisateurs bénéficieraient davantage.

Éviter plus //go: directives

Je recommande de ne pas ajouter //go: directives //go:embed par rapport à la syntaxe Go normale convaincants. J'encourage respectueusement à reconsidérer cette décision avant la sortie de Go 1.16. (Encore une fois, j'apprécie le retard de cette demande.)

Je commencerai par souligner que j'ai une preuve de concept fonctionnelle CL à CL 276835 , qui modifie l'API d'

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

à

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

De même, les fonctions embed.Bytes et embed.String sont disponibles pour intégrer un seul fichier et l'obtenir sous forme de []byte ou string .

Réponses

De même, une variable embed.Files peut être une variable globale ou locale, selon ce qui est plus pratique dans le contexte.

Avoir embed.Files , etc. permet également de les utiliser dans des contextes d'expression, ce qui peut être encore plus pratique.

C'est une erreur d'utiliser //go:embed dans un fichier source qui n'importe pas "embed" (le seul moyen de violer cette règle implique la supercherie d'alias de type).

Avec embed.Files , etc., il s'agit d'une erreur en raison des fonctions exportées par package embed.

Goimports (et gopls, etc.) peuvent apprendre cette règle et ajouter automatiquement l'importation dans n'importe quel fichier avec un //go:embed si nécessaire.

Aucune logique goimports ou gopls spéciale n'est nécessaire pour savoir que l'importation de "embed" est le bon moyen de corriger les utilisations de embed.Files , etc.

Cette approche résout le problème de vérification de type - il ne s'agit pas d'un changement de langage complet - mais elle présente toujours une complexité d'implémentation importante.

Notamment, CL 276835 est une suppression nette de code. En particulier, le code du compilateur (qui devra être réimplémenté dans gccgo et d'autres compilateurs) est beaucoup plus simple.

Je pense aussi qu'il sera plus facile d'enseigner go / types sur la sémantique nuancées de embed.Files , etc. (c. -à- qu'ils acceptent seulement cordes arguments littéral), que de l' enseigner au sujet de //go:embed .

La commande go aurait besoin d'analyser l'intégralité du fichier source Go pour comprendre quels fichiers doivent être rendus disponibles pour l'intégration. Aujourd'hui, il n'analyse que le bloc d'importation, jamais les expressions Go complètes

Avec CL 276835, le comportement est le même qu'à l'astuce : la commande go analyse l'intégralité du fichier source Go pour les fichiers qui importent le package intégré, et uniquement les importations pour les fichiers qui ne le font pas.

Certes, pour les fichiers qui importent embed, CL 276835 effectue une analyse complète et un parcours, tandis que tip effectue une analyse de chaîne plus efficace pour les commentaires //go:embed . Je pense qu'un algorithme en une seule passe plus optimisé pour trouver des appels embed.Files est faisable si vous le souhaitez.

Les utilisateurs ne sauraient pas non plus quelles contraintes sont imposées aux arguments de ces appels spéciaux : ils ressemblent à des appels Go ordinaires, mais ils ne peuvent prendre que des littéraux de chaîne, pas des chaînes calculées par le code Go, et probablement même pas des constantes nommées (ou go aurait besoin d'un évaluateur d'expression Go complet).

Cela ne me semble pas très différent de la directive //go:embed : les utilisateurs n'ont aucune attente quant aux arguments qu'ils sont autorisés à utiliser jusqu'à ce qu'ils l'utilisent pour la première fois. De plus, dans tous les cas, les utilisateurs recevront des messages d'erreur du compilateur s'ils l'utilisent de manière incorrecte, mais les IDE et autres outils fourniront automatiquement de meilleurs godocs et références croisées pour embed.Files , etc.

@mdempsky FS après une importation de points de embed est du même type. Donc, ce cas particulier semble être un simple "oui, ça va" (j'ai essayé et ça marche déjà). De même, byte est un alias pour uint8 , donc []uint8 est également du même type que []byte , bien que de manière assez surprenante, cela ne fonctionne

Je pense que la sémantique la plus proche de Go pour les variables //go:embed serait celle des littéraux composites : chaque exécution génère une nouvelle copie.

Je suis plutôt d'accord, c'était aussi mon attente. Au début, je pensais que cela pouvait être un artefact d'analyse d'échappement et que le compilateur réalisait que vous ne modifiiez pas les données, mais non :

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

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

Cependant, "chaque déclaration var crée une nouvelle variable" a aussi ses problèmes, car cela signifie un code comme celui-ci

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

allouerait et copierait inutilement. Peut-être que c'est bien et que les avantages d'une sémantique plus claire l'emportent. Mais c'est peut-être aussi un signe pour interdire les []byte locaux comme vous le mentionnez.

Je m'attends également à ce qu'il soit plus facile d'enseigner à go/types la sémantique nuancée de embed.Files, etc.

Seules les constantes de chaîne au moins peuvent même être effectuées dans le système de types Go.

Mais, est-ce que go/types même besoin de connaître les fichiers intégrés, avec //go:embed ? Le type de ces variables est assez clair, après tout (à l'exception des intégrations locales []byte , comme discuté).

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

Est-ce vraiment censé être modifiable ? Je ne peux pas penser à un seul cas d'utilisation où je voudrais intégrer un actif statique, mais le muter au moment de l'exécution, donc j'ai l'impression que cela ne fera qu'activer les bogues. J'aurais été plus heureux de le pousser dans .rodata et de paniquer si quelque chose tentait de le faire muter.

(Invitée par x[0] = 'x' ci-dessus.)

@Merovius a écrit :

@mdempsky FS après une importation de points de embed est du même type. Donc, ce cas particulier semble être un simple "oui, ça va" (j'ai essayé et ça marche déjà).

Merci. J'ai oublié comment fonctionnent les importations de points dans le compilateur, j'ai donc mal lu le code. J'aurais dû essayer avant d'inclure cet exemple.

Cependant, je dirais que la sémantique telle qu'elle est implémentée est bonne pour le moment - nous pouvons toujours autoriser plus de types et/ou d'alias et/ou même "le même type sous-jacent" plus tard, si le besoin s'en fait sentir.

[]byte et []uint8 sont des types identiques sous la spécification Go, tout comme d'innombrables autres types construits à l'aide d'alias de type. S'il n'est pas acceptable pour un compilateur de traiter embed.Files("foo" + ".txt") différemment de embed.Files("foo.txt") , alors le même argument devrait s'étendre pour ne pas lui permettre de traiter les types alias différemment.

Mais, est-ce que go/types même besoin de connaître les fichiers intégrés, avec //go:embed ?

Non, il n'en a pas besoin, tout comme il n'a pas non plus besoin de connaître la sémantique spéciale de embed.Files . Mais pour l'une ou l'autre syntaxe, il pourrait toujours être utile que go/types puisse avertir d'une mauvaise utilisation.

--

@tv42 a écrit :

Est-ce vraiment censé être modifiable ?

Évidemment. Le compilateur Go s'arrange spécifiquement pour que les string et embed.FS soient dédupliqués et placés dans rodata, tandis que les []byte ne le sont pas :

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

Cependant, il est vrai que la proposition ne semble pas avoir abordé ce point.

Merci pour les commentaires réfléchis. J'ai déposé deux problèmes à suivre :

#43216 - Supprimer la prise en charge des directives d'intégration sur les variables locales
#43217 - définit les alias de type String et Bytes qui doivent être utilisés avec //go:embed

Je n'ai pas signalé de problème pour la syntaxe //go:embed elle-même. Je voulais dire pourquoi ici, pour que tout le monde n'ait pas l'air de rejeter ça.

À l'époque où je bloguais sur le processus de proposition, j'ai passé beaucoup de temps à chercher des moyens de (et de ne pas) gérer les décisions dans de grands groupes. Une source que j'ai trouvée qui avait beaucoup de sens pour moi était le post de John Ousterhout sur la prise de décision ouverte . Tout le post vaut la peine d'être lu, mais je vais juste citer ici la section sur la reconsidération :

La dernière règle pour une prise de décision ouverte est de s'assurer de ne pas reconsidérer une décision à moins qu'il n'y ait de nouvelles informations importantes. Cette règle comporte deux parties. Tout d'abord, vous devez être prêt à corriger les décisions qui s'avèrent fausses de manière significative. C'est particulièrement vrai dans les startups : de nombreuses décisions doivent être prises sans information complète et inévitablement certaines d'entre elles se révéleront fausses. Une mauvaise décision qui est corrigée rapidement cause peu ou pas de dommages, mais une mauvaise décision qui n'est pas corrigée peut être catastrophique.

D'un autre côté, vous ne devriez pas reconsidérer une décision à moins que de nouvelles informations importantes n'aient été révélées depuis que la décision initiale a été prise. Si aucune nouvelle information n'est disponible, alors reconsidérer une décision produira probablement le même résultat que la décision initiale, faisant perdre du temps à tout le monde. Il n'est pas rare que des employés viennent me voir des semaines après qu'une décision a été prise : « John, j'ai voté contre la décision de XYZ, et plus j'y pense, plus je suis convaincu que c'était faux ; je pense vraiment que nous devons reconsidérer cela." Ma réponse est « Quelles nouvelles informations avez-vous ? » Si la réponse est "aucun", nous ne reconsidérons pas. Dans des situations comme celle-ci, il est utile d'avoir une trace des arguments présentés au cours de la discussion, afin que vous puissiez vérifier que les nouvelles informations sont vraiment nouvelles. Si vous rendez trop facile la réouverture des décisions, vous finissez par vaciller d'avant en arrière où aucune décision n'est jamais définitive et les employés hésitent à mettre en œuvre les décisions parce qu'ils ne croient pas qu'elles sont permanentes.

Si vous voulez vous assurer de ne pas reconsidérer un grand nombre de décisions, assurez-vous de recueillir largement les commentaires au cours du processus de prise de décision. Si vous n'obtenez pas suffisamment d'informations, vous augmentez la probabilité que de nouvelles informations importantes surviennent après la décision, ce qui signifie que vous devrez reconsidérer votre décision. Si vous faites un bon travail de collecte d'informations, il est beaucoup moins probable que vous deviez revoir vos décisions.

Cela a vraiment résonné en moi : le processus de proposition Go (comme les grands projets open source en général) est un système avec une charge offerte beaucoup plus élevée que la capacité de travail, il est donc important que nous (pas d'accord si nécessaire et) nous engagions et passions à la décision suivante .

string et []byte ont été ajoutés assez tard dans le processus d'examen de ce problème, en réponse aux premiers commentaires, et nous n'avons clairement pas réfléchi à toutes les implications de ceux-ci. J'ai donc déposé ces deux nouveaux problèmes, #43216 et #43217.

D'un autre côté, la syntaxe //go:embed était au cœur des discussions initiales et a été longuement discutée, à la fois pour et contre. Je ne pense pas qu'il y ait de "nouvelles informations significatives" qui devraient nous amener à reconsidérer entièrement cette syntaxe, donc dans l'intérêt d'aller de l'avant et de garder à l'esprit les conseils d'Osterhout sur la reconsidération, je laisse cela de côté.

Merci encore d'avoir signalé les problèmes de chaîne et de []octet !

Évidemment. Le compilateur Go s'arrange spécifiquement pour que les string et embed.FS soient dédupliqués et placés dans rodata, tandis que les []byte ne le sont pas :

@rsc as -tu pensé à ce dernier point par hasard ? Avec l'implémentation actuelle, il pourrait devenir une "meilleure pratique" d'utiliser une chaîne au lieu de []octet dans le but de produire de meilleurs binaires. Sommes-nous d'accord avec ça?

Je ne comprends pas pourquoi ce serait une "meilleure pratique". Pour moi, cela revient à dire que c'est la "meilleure pratique" de créer des constantes d'erreurs sentinelles, par conséquent, nous ne devrions pas autoriser les variables de type erreur à l'échelle du paquet - en ce sens que je ne suis pas d'accord avec le fait que cela soit une bonne pratique et avec la restriction supplémentaire en tant que Solution.

Je pouvais voir un argument pour n'utiliser que string dans les vars locales. Mais dans les variables à portée de package, la sémantique est claire et bien définie et je ne recommanderais pas plus d'utiliser une []byte que je ne recommanderais d'utiliser toute autre variable []byte .

@mvdan , si les gens string au lieu de []byte comme meilleure pratique, je considérerais cela comme une bonne chose. string est le type Go approprié pour "une chaîne d'octets immuable", tandis que []byte est le type approprié pour "une chaîne d'octets mutable" ou "une chaîne d'octets de mutabilité indéterminée".

Pour les cas où vous avez une valeur de type string (immuable) et que vous souhaitez l'utiliser comme type []byte (indéterminé), vous pouvez déjà utiliser unsafe pour le faire correctement . (Voir, par exemple, mon unsafeslice.OfString ). Peut-être devrions-nous ajouter une bibliothèque standard prise en charge pour cette opération, mais cela semble être une proposition distincte.

Il semble donc bien de toujours utiliser le type string si vous voulez vraiment que la valeur soit en lecture seule.

@Merovius @bcmills vous soulevez de bons points, et pour être clair, je ne m'y oppose pas. Je veux juste m'assurer que les concepteurs de la proposition réfléchissent à cette distinction avant la version finale.

Je ne pense vraiment pas que la déduplication va se poser beaucoup dans la pratique. (Dans quel cadre plusieurs packages vont-ils intégrer exactement les mêmes fichiers ?) Les gens doivent utiliser le formulaire dont ils ont besoin et ne pas s'inquiéter de "la chaîne signifie que mes binaires sont plus petits", car en général, je ne pense pas que ce sera vrai.

Quelques personnes ont posé des questions sur les ETags. Nous avons manqué de temps pour cela, mais j'ai déposé une proposition sur https://github.com/golang/go/issues/43223 et j'espère que cela conduira à une bonne idée qui pourra entrer dans Go 1.17. Désolé de ne pas pouvoir l'obtenir dans ce tour.

Merci pour le dépôt #43216 et #43217. Si ceux-ci sont acceptés, ils répondront en grande partie à mes préoccupations en suspens avec la proposition //go:embed .

D'un autre côté, la syntaxe //go:embed était au cœur des discussions initiales et a été longuement discutée, à la fois pour et contre. Je ne pense pas qu'il y ait de "nouvelles informations significatives" qui devraient nous amener à reconsidérer entièrement cette syntaxe, donc dans l'intérêt d'aller de l'avant et de garder à l'esprit les conseils d'Osterhout sur la reconsidération, je laisse cela de côté.

Je peux respecter le fait de ne pas vouloir revenir sur des questions qui ont déjà été décidées au cours de discussions approfondies. Mais après avoir passé en revue la discussion qui a eu lieu sur #35950, le fil Reddit , et ici, je ne pense pas qu'ils justifient la décision d'utiliser //go:embed .

Voici les commentaires que j'ai trouvés concernant la syntaxe pour indiquer les fichiers à intégrer :


18 Problème GitHub et commentaires Reddit

  • https://github.com/golang/go/issues/35950#issuecomment -561443566 "L'approche //go:embed introduit également un autre niveau de complexité. Vous devrez analyser les commentaires magiques pour même vérifier le type L'approche "embed package" semble plus conviviale pour l'analyse statique." (Remarque : La proposition révisée //go:embed facilite la saisie du code de vérification, mais ce n'est toujours pas trivial si un analyseur veut réellement voir les chaînes utilisées, car elles ne sont pas en go/ast.)
  • https://github.com/golang/go/issues/35950#issuecomment -561450136 "C'est un argument très fort pour l'utilisation d'un package. Cela le rend également plus lisible et documentable, car nous pouvons tout documenter avec godoc ordinaire, plutôt qu'au fond de la documentation de cmd/go." (Remarque : je pense que cela est principalement traité par #43217, car les types d'intégration de package fournissent un point de référence facile.)
  • https://github.com/golang/go/issues/35950#issuecomment -561840094 "Y a-t-il une raison pour laquelle cela ne pourrait pas faire partie de go build / link ... par exemple go build -embed example=./path/example.txt et un package qui expose y accéder (par exemple embed.File("example") , au lieu d'utiliser go:embed ?" (Suggère une syntaxe de fonction sur la directive //go: . Vote négatif, mais je soupçonne parce qu'il suggère go build drapeaux.)
  • https://github.com/golang/go/issues/35950#issuecomment -561726107 "Je ne suis pas particulièrement fan d'un commentaire qui compile en code, mais je trouve aussi que le pseudo-paquet qui affecte la compilation est un peu étrange aussi. Si l'approche par répertoire n'est pas utilisée, il serait peut-être un peu plus logique d'avoir une sorte de déclaration de niveau supérieur intégrée dans le langage. Cela fonctionnerait de la même manière pour importer, mais ne prendrait en charge que les chemins locaux et nécessiterait un nom pour lui être attribué. (N'aime ni //go: ni les nouvelles fonctions intégrées, mais préfère toujours le code plutôt que les commentaires.)
  • https://github.com/golang/go/issues/35950#issuecomment -561856033 "De plus, étant donné que les directives //go:generate ne s'exécutent pas automatiquement sur go build, le comportement de go build peut sembler un peu incohérent : //go:embed fonctionnera automatiquement mais pour //go:generate vous devez exécuter go generate manuellement ( //go:generate peut déjà interrompre le flux go get s'il génère les fichiers .go nécessaires à la construction). " (Inclus pour être complet, mais nous avons déjà des directives //go: qui affectent et n'affectent pas le comportement de go build même sans //go:embed , donc je ne trouve pas cet argument convaincant.)
  • https://github.com/golang/go/issues/35950#issuecomment -562005821 "Je voterais pour le nouveau paquet par opposition à la directive. Beaucoup plus facile à maîtriser, plus facile à manipuler/gérer et beaucoup plus facile pour documenter et étendre. Par exemple, pouvez-vous facilement trouver la documentation pour une directive Go telle que "go:generate" ? Qu'en est-il de la documentation pour le paquet "fmt" ? Voyez-vous où je veux en venir ?" (Encore une fois, principalement abordé par #43217.)
  • https://github.com/golang/go/issues/35950#issuecomment -562200553 "Un argument en faveur d'un pragma de commentaire (//go:embed) au lieu d'un nom de répertoire spécial (static/) : un commentaire nous permet intégrer un fichier dans l'archive de test pour un package (ou l'archive xtest) mais pas la bibliothèque en cours de test. Le commentaire doit juste apparaître dans un fichier _test.go." (Remarque : l'argument est contre le répertoire spécial, pas pour //go:embed particulier ; le même argument s'étend aux fonctions embed.Files , etc.)
  • https://github.com/golang/go/issues/35950#issuecomment -562235108 "J'aime l'approche package embed pour son utilisation de la syntaxe Go"
  • https://github.com/golang/go/issues/35950#issuecomment -562713786 "L'approche import "C" a déjà créé un précédent pour les chemins d'importation "magiques". OMI, cela a plutôt bien fonctionné. "
  • https://github.com/golang/go/issues/35950#issuecomment -562768318 "Plusieurs personnes ont parlé de fournir automatiquement une API pour lire les fichiers intégrés, au lieu d'avoir besoin d'un autre signal comme un commentaire magique. Je pense que cela devrait être la voie à suivre, car elle offre une syntaxe de programmation familière à l'approche. Opter pour un package spécial, éventuellement runtime/embed comme mentionné précédemment, satisferait cela et permettrait une extensibilité facile à l'avenir. "
  • https://github.com/golang/go/issues/35950#issuecomment -562959330 "Au lieu de réutiliser les commentaires (qui finissent par être éparpillés un peu partout et, sur une note personnelle, se sentent juste dégoûtants), [... ]." (Continue en suggérant d'utiliser les fichiers go.mod pour énumérer les fichiers intégrés. Pour être juste, il soutient également que "Cela permet de contourner des problèmes tels que la nécessité de restreindre le package magique pour n'autoriser que les littéraux de chaîne comme arguments, mais cela signifie qu'il est plus difficile de vérifier si les actifs intégrés sont en fait utilisés n'importe où ou finissent par être un poids mort.")
  • https://github.com/golang/go/issues/35950#issuecomment -562966654 "Encore une autre idée : au lieu d'ajouter un nouveau type de verbe intégré dans go.mod, nous pourrions introduire un nouveau type de package, un package de données , qui est importé et utilisé dans go.mod de la manière habituelle. Voici un croquis d'homme de paille." (Préférence pour le code sur les commentaires.)
  • https://github.com/golang/go/issues/35950#issuecomment -563156010 "Voici quelque chose qui n'a pas encore vu mentionné : l'API pour les outils de traitement de source Go (compilateur, analyseurs statiques) est aussi importante que l'API d'exécution Ce type d'API est une valeur fondamentale de Go qui aide à développer l'écosystème (comme go/ast / go/format et go mod edit ). [...] Dans le cas de un package spécial, je ne vois rien à changer dans l'analyse go.mod ( go mod outils) ou go/ast parser."
  • https://github.com/golang/go/issues/35950#issuecomment -601748774 Suggère la liste des fichiers de ressources go.res. Utilise du code, pas des commentaires.
  • https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fyv9gxw/ "Pourquoi avons-nous besoin du commentaire //go:embed, par exemple binclude fait binclude.Include(filename) pour inclure un fichier/répertoire quoi environ fs.Embed(filename) ?"
  • https://github.com/golang/go/issues/41191#issuecomment -686625127 "À mon avis, ajouter du sucre syntaxique dans la langue pour prendre en charge ce changement d'outil est un changement de langue. Je suis sûr que cela est évident pour les autres, mais c'est effectivement un commentaire en tant que code. Je pense fortement que la magie/sucre nuit à la simplicité et à la lisibilité du langage ; il est très facile de rater un commentaire magique qui intègre un fichier. "
  • https://github.com/golang/go/issues/41191#issuecomment -690423900 "J'ai les mêmes préoccupations que //go:embed est susceptible d'être plus courant. Il est peut-être temps d'envisager une syntaxe différente pour le compilateur directives ?"
  • https://github.com/golang/go/issues/41191#issuecomment -690522509 "Je partage vos inquiétudes concernant les commentaires magiques."

Au fur et à mesure que je lis les commentaires, ils semblent toujours privilégier la syntaxe Go, avec la mise en garde de vouloir éviter les modifications qui casseront les outils. Je n'ai vu personne plaider pour //go:embed comme orthographe, autant que l'accepter tel quel. L'ajout de nouveaux éléments intrinsèques du compilateur avec des stubs de compatibilité descendante pour les vérificateurs de type hérités semble plus conforme à la préférence exprimée par les participants à la discussion que plus //go: directives

Dans le style des sondages pouces vers le haut / pouces vers le bas dans #43216 et #43217 :

  • Bravo (👍) si vous préférez les nouveaux éléments intrinsèques du compilateur (par exemple, CL 276835 ):

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

    Voir embed_test.go pour plus d'exemples d'utilisation.

    (Remarque : les arguments doivent être des littéraux de chaîne , pas simplement des valeurs de chaîne. Autrement dit, les constantes de chaîne déclarées comme const logo = "logo.jpg" ou les expressions de chaîne constantes comme "logo" + ".jpg" ne sont pas non plus autorisées. Cependant, embed.Files , etc. peut être utilisé dans n'importe quel contexte, pas seulement pour initialiser des variables.)

  • Pouce vers le bas (👎) si vous préférez les nouvelles directives du compilateur (c'est-à-dire la proposition actuelle) :

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

@mdempsky J'ai l'impression que Russ a été assez clair, ce qui serait nécessaire pour justifier un renversement de la décision pour lui - de nouvelles informations. Je pense que rassembler les commentaires précédents n'est clairement pas cela. Aucune infraction prévue.

Il n'y a pas de précédent pour ces nouveaux types de fonctions, n'est-ce pas ? C'est-à-dire quelque chose qui ressemble à une fonction de package normale mais qui est en fait un type spécial de fonction intégrée qui ne peut être appelée que d'une certaine manière ?

Vous dites "intrinsèque" mais les intrinsèques actuels se comportent exactement comme les fonctions Go normales.

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

Habiller de nouvelles directives pour ressembler à des fonctions Go mais avoir une syntaxe différente (plus restrictive) que les fonctions Go semble être une mauvaise option d'où je suis assis. Je pense que embed devrait être décrit dans la spécification, d'une part, contrairement aux directives //go: .

(Vous pouvez approximer les "seuls arguments littéraux autorisés" dans le code Go normal en créant une fonction qui prend des arguments type internalString string non exportés, mais je suppose que ce n'est pas ce que vous proposez puisque votre CL modifie le analyseur et vérificateur de type.)

@Merovius Selon le processus de proposition de Go :

Consensus et désaccord

L'objectif du processus de proposition est d'atteindre un consensus général sur le résultat en temps opportun.

Si un consensus général ne peut être atteint, le groupe d'examen de la proposition décide de l'étape suivante en examinant et en discutant de la question et en parvenant à un consensus entre eux. Si même un consensus au sein du groupe d'examen de la proposition ne peut être atteint (ce qui serait extrêmement inhabituel), l'arbitre (rsc@) examine la discussion et décide de l'étape suivante.

Dans ma lecture des commentaires, le consensus semblait favoriser la syntaxe du code Go. Si le consensus maintenant est de s'en tenir à //go:embed , je le respecte. Mais je ne pense pas que le processus documenté ait justifié la décision initiale d'aller de l'avant avec //go:embed .

(Pour le moment, les résultats du sondage favorisent faiblement les nouvelles fonctions par rapport aux nouvelles directives, mais pas de beaucoup. Si les pouces levés ne sont pas plus nombreux que les pouces vers le bas au moins 2:1, je suis d'accord pour laisser tomber cela.)

@cespare

Il n'y a pas de précédent pour ces nouveaux types de fonctions, n'est-ce pas ? C'est-à-dire quelque chose qui ressemble à une fonction de package normale mais qui est en fait un type spécial de fonction intégrée qui ne peut être appelée que d'une certaine manière ?

Il y a à la fois les fonctions prédéclarées de l'univers et les fonctions de package unsafe.

Vous pouvez affirmer que le package non sécurisé est documenté dans la spécification Go, mais je soutiens que cela n'a pas besoin de l'être. Go avait l'habitude de prendre en charge les modes où le package non sécurisé n'était pas disponible pour les utilisateurs, et même aujourd'hui, le package non sécurisé a des fonctionnalités et des restrictions qui ne sont documentées que dans la documentation du package, pas dans la spécification Go.

Il existe également des fonctions internes dans l'environnement d'exécution Go qui ne sont implémentées que par le compilateur Go. Par exemple, getg , getcallerpc , getcallersp et getclosureptr .

(Vous pouvez approximer les "seuls arguments littéraux autorisés" dans le code Go normal en créant une fonction qui prend des arguments de chaîne internalString de type non exporté,

Je pense que ce serait un ajout raisonnable pour affiner davantage le comportement des vérificateurs de type hérités.

mais je suppose que ce n'est pas ce que vous proposez puisque votre CL apporte des modifications à l'analyseur et au vérificateur de type.)

CL 276835 ne modifie pas l'analyseur, sauf pour supprimer le nouveau code d'analyseur ajouté pour //go:embed . Cela change le vérificateur de type, mais de manière comparable à //go:embed auparavant.

Il serait facile d'étendre go/types pour être au courant de l'intégration de package, mais j'ai choisi de ne pas le faire pour CL 276835 spécifiquement pour montrer qu'il fonctionne toujours (par exemple, cmd/vet n'échoue pas sur les tests unitaires de package embed).

@mdempsky Vous pouvez être en désaccord sur la question de savoir si un consensus a été atteint ou non à ce moment-là. Tout comme vous pourriez être en désaccord avec la décision elle-même. Je ne pense pas que cela change vraiment les choses, cependant. En fin de compte, « il y a consensus » est aussi une décision qui a été prise. Et exactement les mêmes points concernant l'exigence de nouvelles informations pour un renversement s'appliquent à cette décision.

Avoir besoin de satisfaire chaque personne est un vecteur DDoS - à la fois en ce qui concerne la décision et le processus avec lequel elle a été prise.

FTR, la question de l'outillage par rapport au changement de langue a été discutée , tout comme le compromis entre "chaîne-littéral vs chaîne-constante" ("chaîne-littéral" nécessite de la magie dans le vérificateur de type, "chaîne-constante" nécessite la go pour faire la vérification de type - les commentaires n'ont besoin ni de l'un ni de l'autre). Donc, encore une fois, il n'y a vraiment rien de nouveau ici. Vous pourriez être en désaccord avec le résultat de cette décision ou le processus avec lequel elle a été prise - mais les arguments que vous mentionnez ont été pris en compte lors de la prise de décision.

Avoir besoin de satisfaire chaque personne est un vecteur DDoS - à la fois en ce qui concerne la décision et le processus avec lequel elle a été prise.

Je n'exige pas que je sois personnellement satisfait, et je trouve insultant que vous qualifiiez mes messages comme tels. Plus tôt, vous m'avez également parlé comme si je ne connaissais pas le langage Go ou le compilateur Go. S'il vous plaît arrêtez avec la condescendance.

J'ai énuméré ci-dessus de nombreux commentaires où les gens ont presque universellement exprimé une préférence pour ne pas ajouter de nouvelles directives //go: , alors que personne n'avait fait de commentaires affirmatifs en leur faveur. En tant que tel, la préférence exprimée écrasante de la communauté m'a semblé favoriser la syntaxe Go, et mon commentaire plaidait pour la défense de leurs préférences déclarées.

Cependant, dans l'état actuel des choses, https://github.com/golang/go/issues/41191#issuecomment -747095807 a plus de pouces vers le bas que de pouces vers le haut. Cela me surprend, car cela semble incompatible avec tous les commentaires lors de la discussion précédente. Mais je suis heureux d'accepter que la question a été directement abordée, et (surtout en tant que personne qui sera impliquée dans le support à long terme de cette fonctionnalité dans le compilateur Go), je suis plus heureux maintenant de prendre en charge //go:embed vu qu'il s'agit bien de la préférence de la communauté et pas seulement de la préférence des auteurs de la proposition. Ce résultat n'aurait pas été atteint si la discussion avait été interrompue, comme vous semblez le vouloir.

FTR, la question de l'outillage par rapport au changement de langue a été discutée , tout comme le compromis entre "chaîne-littéral vs chaîne-constante" ("chaîne-littéral" nécessite de la magie dans le vérificateur de type, "chaîne-constante" nécessite la go pour faire la vérification de type - les commentaires n'ont besoin ni de l'un ni de l'autre).

Ce commentaire est sans rapport avec les arguments que je faisais. L'orthographe alternative que j'ai prototypée dans CL 276835 a exactement les mêmes propriétés techniques que l'orthographe //go:embed existante : les outils qui doivent savoir quels fichiers doivent être intégrés devront être mis à jour pour traiter les fichiers source Go différemment (par exemple, pour à propos de l'utilisation abusive des commentaires //go:embed ou de la fonction intégrée embed.Bytes ), alors que les outils existants peuvent continuer à traiter le code de manière raisonnable sans s'en soucier (par exemple, go/types ignorera les commentaires //go:embed mais ne détectera pas s'il est appliqué à des types de variables incorrects, et il peut vérifier le type embed.Bytes utilisant les déclarations de stub mais il ne saura pas rejeter tous les appels qui utilisent des arguments autres que des chaînes littérales).

La question de savoir si l'un ou l'autre de ceux-ci est un "changement de langue" est plus philosophique que technique.

(L'argument de Russ selon lequel "la validité d'un programme ne change pas" sous la proposition //go:embed est également incorrect. https://github.com/golang/go/issues/41191#issuecomment-747799509 donne un exemple de un package qui était et est valide selon la spécification Go et a également été accepté par les versions de la chaîne d'outils Go jusqu'à Go 1.15, mais ne sera plus valide dans Go 1.16.)

En tant que personne qui a donné un 👍 à la proposition de Matt d'utiliser var website = embed.Files("logo.jpg", "static/*") , ma préoccupation concernant l'utilisation du formulaire de commentaire ( //go:embed logo.jpg static/* ) est la "facilité d'utilisation".

Par exemple, ces 2 exemples de programme produiraient 2 choses différentes, simplement parce qu'une "importation" a été manquée :

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
}

En forçant le développeur à utiliser l'importation via la sémantique du langage, vous minimisez les problèmes où les fichiers intégrés ne fonctionnent pas comme prévu car ils ne sont pas initialisés comme prévu.

@kushieda-minori Bien que je pense que ma proposition est également plus facile à utiliser, le premier exemple ne se compile déjà pas à l'astuce :

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

Je pense que ce problème est également atténué par #43217, car vous devez importer "embed" pour déclarer les variables de type embed.Bytes et embed.String toute façon. Et le compilateur (mais pas go/types ou cmd/vet) signale également déjà une erreur si vous appliquez la directive //go:embed à une variable d'un type incorrect.

@mdempsky , cependant, la compilation accidentelle sur une ancienne version de Go n'échouera pas et pourrait donner une fausse impression que l'intégration a fonctionné.

Les anciennes versions de Go n'ont pas d'intégration de package, donc le import "embed" échouera. C'est vrai qu'il y a un risque que les utilisateurs puissent écrire :

package p

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

et il sera accepté en silence par Go 1.15 et versions antérieures. Mais il ne sera pas accepté par Go 1.16 et plus récent. Il n'y a au moins aucun programme qui compilera à la fois avec Go 1.15 et Go 1.16 et aura une sémantique différente (au moins en raison de l'intégration de paquet).

Je pense qu'un correctif (partiel) approprié ici serait que cmd/compile arrête d'accepter les directives inconnues //go: . Par exemple, une autre limitation de la proposition actuelle relative à la syntaxe intégrée de Go est que si vous saisissez mal la directive //go:embed (disons //go:enbed , //go;embed ou // go:embed avec un espace), il sera également ignoré en silence. (Alors que des fautes de frappe comme enbed.Bytes("foo.txt") provoqueraient une erreur de vérification de type, même avec des go/types non modifiés.)

Excellent point sur les directives go: non validées. Si cela était appliqué, cela aiderait à atténuer les erreurs de frappe difficiles à repérer.

Une autre pensée que je viens d'avoir est que mon outillage est configuré pour ajouter/supprimer automatiquement des importations selon les besoins. Si mon outillage est obsolète, dois-je lutter contre la suppression de l'import embed « inutilisé » ? J'ai réalisé que c'était résolu si j'utilisais le embed.String etc, mais l'utilisation régulière de []byte] et de string est censée être complètement valide. Cela pourrait être frustrant pour un nouveau gopher qui sélectionne des extraits Web pour faire fonctionner quelque chose s'il ne voit pas les alias embed.* spécifiques.

mais l'utilisation régulière de []byte] et string est censée être complètement valide.

Ils ne le seront pas si le #43217 est accepté. Je recommande de lire les articles #43216 et #43217. Ils ont tous les deux reçu un soutien extrêmement positif jusqu'à présent et me semblent très susceptibles d'être acceptés. (Je ne fais pas partie du comité d'examen des propositions.)

Merci, quand j'ai lu le n°43217 pour la première fois, j'ai raté le mot clé
"avoir" pour utiliser les types embed.* .

Je pense que ma seule préoccupation est la dernière que vous avez signalée.

Le jeu. 17 déc. 2020, 20:24 Matthew Dempsky [email protected]
a écrit:

mais l'utilisation régulière de []byte] et d'une chaîne est censée être complètement valide.

Ils ne le seront pas si #43217 https://github.com/golang/go/issues/43217 est
accepté. Je recommande de lire le #43216
https://github.com/golang/go/issues/43216 et #43217
https://github.com/golang/go/issues/43217 . Ils ont tous les deux reçu
un soutien extrêmement positif jusqu'à présent, et semblent très susceptibles d'être acceptés
tome. (Je ne fais pas partie du comité d'examen des propositions.)

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/golang/go/issues/41191#issuecomment-747808153 , ou
Se désabonner
https://github.com/notifications/unsubscribe-auth/ADLZABJWBJX475BVYDVD6ODSVKVOTANCNFSM4QTHVTUA
.

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