Go: incrustar, cmd / go: agregar soporte para archivos incrustados

Creado en 2 sept. 2020  ·  114Comentarios  ·  Fuente: golang/go

En julio, @bradfitz y yo publicamos un borrador de diseño para archivos incrustados . El documento se vincula a un video, un código prototipo y una discusión de Reddit.

Los comentarios sobre ese diseño han sido abrumadoramente positivos.

Propongo adoptar el diseño del borrador de archivos incrustados para Go 1.16, con una adición, sugerida en la discusión, para simplificar el caso del acceso directo a los bytes en un solo archivo incrustado.

Siempre que un archivo importe "embed" ( import _ "embed" si es necesario), se le permitirá usar //go:embed nombrando un solo archivo (no se permiten patrones globales o coincidencia de directorios) para inicializar una variable simple string o []byte :

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

Se requiere la importación para marcar el archivo como que contiene //go:embed líneas y necesita procesamiento. Se puede enseñar esta regla a Goimports (y gopls, etc.) y agregar automáticamente la importación en cualquier archivo con un //go:embed según sea necesario.

El diseño de los archivos incrustados depende del diseño preliminar de la interfaz del sistema de archivos, que también propuse adoptar en el n. ° 41190.

Este problema es _sólo_ acerca de la adopción del diseño de archivos incrustados , bajo el supuesto de que también se adopta el diseño de la interfaz del sistema de archivos. Si se acepta esta propuesta antes de que se acepte el diseño de la interfaz del sistema de archivos, simplemente esperaríamos el diseño de la interfaz del sistema de archivos antes de comenzar a realizar cambios.

Proposal Proposal-Accepted

Comentario más útil

Sin cambios en el consenso, así que aceptado.

Todos 114 comentarios

¿Sería un error tener una directiva //go:embed sin importar embed ?

@jimmyfrasche Sí, de la quinta a la última viñeta de la lista en https://go.googlesource.com/proposal/+/master/design/draft-embed.md#go_embed -directives.

@rsc Tal vez me lo perdí en el borrador, pero no veo la capacidad de incrustar un solo archivo que mencionas en tu comentario.
Además, ¿podría incrustar un solo archivo como una cadena constante también?
Gracias por esta gran propuesta.

@pierrec No está en el borrador del documento (la "única adición" es el texto del comentario anterior). Las cadenas de constantes pueden terminar desempeñando un papel a la hora de decidir si un tipo de programa verifica, lo que significaría que todos los verificadores de tipos tendrían que entender // go: embed'ed consts. Por el contrario, si nos atenemos a las vars, los correctores de tipos no son más sabios y pueden dejarse tranquilos. Parece que probablemente deberíamos ceñirnos a las vars.

¿Hay alguna razón en particular por la que quisieras una constante en lugar de una var? Usarlos debería ser aproximadamente lo mismo en cuanto a eficiencia. (Las referencias a cadenas constantes terminan compilando hasta qué punto son referencias a vars ocultas de todos modos).

Gracias por la explicación. Tiendo a incrustar activos estáticos como cadenas constantes en este momento, por eso pregunté. ¡Yo también estoy bien con las vars!

Interesante, entonces podría hacer algo como:

//go:embed version.txt
var Version string

Y potencialmente incluso tener un comentario //go:generate para generar version.txt. Eso eliminaría un gran caso de uso para makefiles / ldflags.

¿Es un error si no se encuentra el archivo? Si es así, ¿dónde se considera técnicamente que se produce el error? ¿Tiempo de enlace?

¿Podemos asegurarnos de que go: embed se ejecuta después de go: generate para que podamos hacer cosas como generar versiones fácilmente, etc.?

¿Podemos asegurarnos de que go: embed se ejecuta después de go: generate para que podamos hacer cosas como generar versiones fácilmente, etc.?

Según tengo entendido, go:generate ocurrirá con go generate mientras que go:embed sucederá con go build .

@carlmjohnson Sí, siempre es un error decir //go:embed foo donde foo no existe.
El error ocurre al compilar el archivo fuente que contiene esa línea.
(Si eliminara foo después de compilar ese archivo fuente, aún no llegaría a un paso de enlace; el comando go notaría que el paquete necesita reconstruirse porque foo fue eliminado).

Creo que esta propuesta no está completa sin decir algo sobre ETag.
https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fzi0pok/

@ tv42 , sí, haremos que ETag funcione. No estoy seguro de cuál es la forma de eso, pero lo haremos.
(También afirmado en https://github.com/golang/go/issues/35950#issuecomment-685845173.)

Dos Tres cosas que he notado al trabajar con mjibson/esc :

  • Como go:embed no necesita generar archivos go para incrustar como un sistema de archivos de solo lectura, eliminaría el dolor de cambiar las marcas de tiempo en los archivos go:generate ed que desafiaron git porcelain pruebas en CI- muy agradable
  • Una cosa que no encontré en la propuesta pero que necesitaría es la capacidad de recargar en vivo los archivos incrustados durante los ciclos de desarrollo. Usando mjibson/esc actualmente puedo hacer eso indicándole que use el sistema de archivos local (aunque no recogería archivos nuevos) y cambiar el comportamiento usando etiquetas de compilación. Me pregunto qué podría encajar eso en la propuesta.
  • Actualización Otra cosa que recuerdo es que se requiere esc para poder eliminar de forma transparente (partes de) la ruta base para, por ejemplo, exportar una carpeta de activos como raíz web.

Pensamiento tardío : supongo que el segundo punto podría solucionarse junto con la propuesta io/fs en la que usaría el sistema de archivos integrado o en vivo para su inclusión. ¿Implementar la eliminación de rutas como io/fs middleware?

@andig Ya puede eliminar prefijos al servir un sistema de archivos a través de HTTP . Estoy de acuerdo en que la recarga en vivo puede ser realizada por una biblioteca de terceros envolviendo un io/fs .

Una cosa más: si entiendo correctamente, incrustar considerará los archivos localmente en el paquete y prohíbe .. . Mi diseño actual tiene /assets y /server/ donde este último contiene el código del servidor y hoy aloja los archivos generados. Con esta propuesta, la inserción debería moverse a la carpeta raíz, ya que los activos no serían accesibles desde el servidor. Esto impone restricciones de accesibilidad diferentes a las de las importaciones normales. Me preguntaba si esto es necesario por razones de seguridad o si las incrustaciones locales de módulos deberían estar permitidas en general.

Una cosa más: si entiendo correctamente, incrustar considerará los archivos localmente en el paquete y prohíbe .. . Mi diseño actual tiene /assets y /server/ donde este último contiene el código del servidor y hoy aloja los archivos generados. Con esta propuesta, la inserción debería moverse a la carpeta raíz, ya que los activos no serían accesibles desde el servidor. Esto impone restricciones de accesibilidad diferentes a las de las importaciones normales. Me preguntaba si esto es necesario por razones de seguridad o si las incrustaciones locales de módulos deberían estar permitidas en general.

Puede crear un archivo emed.go en su directorio de activos y hacer que los activos estén disponibles como su propio paquete para el resto de su programa.

Otro objetivo explícito es evitar un cambio de idioma. Para nosotros, incrustar activos estáticos parece un problema de herramientas, no un problema de idioma.

Acordado. En mi opinión, agregar azúcar sintáctico en el idioma para respaldar este cambio de herramientas es un cambio de idioma. Estoy seguro de que esto es obvio para los demás, pero esto es efectivamente un comentario como código.

Siento fuertemente que la magia / azúcar resta valor a la simplicidad y legibilidad del lenguaje; es muy fácil perderse un comentario mágico que incrusta un archivo. Si bien una respuesta a esto podría ser fácilmente "está bien, entonces no lo use", este cambio significa que un revisor aún debe estar atento a otros que usen esta función y debe recordar que los comentarios sobre declaraciones de variables pueden romper compilaciones o fallar en tiempo de compilación.

Creo que esto agregará confusión, restará valor a la usabilidad del lenguaje y dará como resultado binarios grandes y opacos sin un beneficio claro (con respecto a la última preocupación, esto incluso conducirá a un anti-patrón de reconstrucción de binarios debido a cambios de archivos simples ). Si go mod permitido para un --withNonGoCodeAssets , creo que esto resolvería las necesidades de la mayoría de los desarrolladores que no quieren escribir pipelines de compilación más complejos (supongo que la distribución del usuario final es un subconjunto más pequeño de el problema para los usuarios).

@tristanfisher , entiendo su punto sobre el cambio de lenguaje frente a las herramientas. Ciertamente está cerca de la línea. La razón por la que lo considero más un cambio de herramientas es que la especificación del idioma no se ve afectada: si un programa es válido no cambia, el proceso de verificación de tipo no cambia. Todo lo que cambia es el valor inicial de esa variable que sigue al comentario. De esta manera, es un poco como la bandera -X del enlazador, que puede establecer el valor inicial de una var de nivel superior de tipo cadena. Está bien que no estemos de acuerdo; Solo quería aclarar mi definición y explicar la distinción que estoy haciendo.

En cuanto a la hinchazón, supongo que tendremos que ver, pero no anticipo que los programas sean mucho más grandes de lo que ya son. La gente _ ya_ ejecuta herramientas que convierten archivos arbitrarios en código Go, los registra en sus repositorios y hace que el compilador los compile. El diseño elimina algunos gastos generales de este proceso, pero no permite nada nuevo. Tal vez la gente lo abuse ahora que es más fácil de hacer, pero a fin de cuentas, no espero que eso sea un gran problema. (Y si alguna dependencia incrusta algo tan grande que hincha sus binarios, siempre puede optar por no usar esa dependencia).

En cuanto a las reconstrucciones debido a cambios de archivos sin formato, los únicos archivos que pueden desencadenar reconstrucciones son los de su propio módulo de nivel superior, ya que las dependencias son inmutables. Si descubrió que las reconstrucciones ocurren con más frecuencia de la que le gustaría, la única explicación es (1) está incrustando archivos y (2) está modificando esos archivos. Tendrías el control total de hacer algo sobre cualquiera de las causas. (Sería completamente diferente si la elección de una dependencia de qué usar de alguna manera le obligara a reconstrucciones adicionales u otros gastos para usted. Pero ese no es el caso aquí).

@rsc Estoy de acuerdo en que está bien que no

Dicho esto, si esto se agrega al ecosistema, estaré agradecido de que se requiera importar embed ; eso es mejor que nada como un "hey, cabeza arriba" al auditar el código. Creo que go mod permitir que no sea .go resolvería la mayoría de los casos de uso (imagino que la mayoría de la gente va a usar archivos glob para servidores web) y también viviría completamente en herramientas.

Creo que su punto con respecto al enlazador es bueno. También ayuda a explicar mis sentimientos sobre esto: si el usuario final (por ejemplo, no alguien que simplemente importa un paquete) está tomando la decisión, no hay forma de sorprenderse por las manchas de no código que aparecen en el viaje. Mis preocupaciones nacen de revisar / emparejar el trabajo de otros y las responsabilidades "tecnológicas", razón por la cual sentí la necesidad de responder.

Creo que "tendremos que ver" lo resume bien (soy más cínico acerca de la hinchazón / mal uso).

Leeré el borrador del diseño esta noche, hasta ahora se ve bien desde la perspectiva de TinyGo.

Solo quería aclarar una cosa:

Por otro lado, proyectos como TinyGo y sistemas de destino U-root con más RAM que disco o flash. Para esos proyectos, la compresión de activos y el uso de descompresión incremental en tiempo de ejecución podrían proporcionar ahorros significativos.

No sé sobre U-root, pero para TinyGo los objetivos principales son los microcontroladores que normalmente tienen mucho más flash que RAM (generalmente un factor de 8 o 16). Un vistazo rápido al borrador del diseño parece sugerir que la idea es mantener los archivos en la memoria de solo lectura, lo que funcionaría bien para estos objetivos: los archivos incrustados se pueden leer directamente desde flash. Lo más probable es que no sea deseable que los objetivos de TinyGo descompriman archivos en tiempo de ejecución.

La propuesta de io / fs de la que depende esto parece estar bloqueada por problemas de Readdir / FileInfo, que se discuten en # 41188 y anteriormente # 40352.

He redactado una API para reemplazarlos en https://github.com/golang/go/issues/41188#issuecomment -686283661

@andig

Una cosa que no encontré en la propuesta pero que necesitaría es la capacidad de recargar en vivo los archivos incrustados durante los ciclos de desarrollo.

embed.Files implementa fs.FS, por lo tanto, todo lo que necesita hacer es usar la etiqueta de compilación dev vs! dev para cambiar una variable entre embed.Files y el FS real.

Presenté # 41265. Ofrece una nueva API ReadDir () para io / fs.

Tengo preocupaciones similares a las de @tristanfisher . Go ha estado usando comentarios mágicos como directivas del compilador durante mucho tiempo (¿desde el principio?), Pero están destinados a casos de esquina y es raro que aparezcan en el código. Dada la popularidad de incrustar contenido estático en binarios de Go, es probable que //go:embed sea ​​más común. ¿Quizás es hora de considerar una sintaxis diferente para las directivas del compilador?

Solo un recordatorio de que cambiar la sintaxis de Go tiene un costo muy alto. Prácticamente todas las herramientas de Go tendrían que actualizarse y / o corregirse para admitir la nueva sintaxis, por ejemplo.

No los considero comentarios mágicos. Las líneas que comienzan con //go: son directivas y podrían definirse como tales en la especificación. No hay mucha diferencia semántica entre //go:embed , @embed , [[embed]] o cualquier otro número de variaciones de sintaxis, excepto que el prefijo //go: ya se trata como no -código por herramientas Go. (mi editor resalta esas líneas de manera diferente, por ejemplo)

@mvdan Si esta propuesta ocurre, la sintaxis de Go ha cambiado. Simplemente se cambió de una manera que no rompe las herramientas existentes. Quizás eso parezca pedante.

@iand no soy quisquilloso con la sintaxis específica de las directivas del compilador. Creo que es necesario formalizarlo en algún momento y especificar las reglas.

Creo que esta propuesta es una buena idea. Resuelve un problema común. Mi preocupación es que el costo de adoptarlo debería ser un poco más explícito.

@jonbodner Comparto sus preocupaciones sobre los comentarios mágicos. Pero, hasta cierto punto, las reglas están especificadas en # 37974.

@networkimprov , esta no es la propuesta de io / fs. Deje de comentar sobre ReadDir aquí.

@jonbodner

No soy quisquilloso con la sintaxis específica de las directivas del compilador. Creo que es necesario formalizarlo en algún momento y especificar las reglas.

Solo señalaría que tomamos la decisión de usar //go: para marcar las directivas de la cadena de herramientas Go cuando
agregamos (el uso limitado) //go:nointerface anotación en 2012.
Agregamos //go:noescape para los autores de ensamblajes en 2013.
Agregamos //go:generate en 2014.
Es probable que también agreguemos //go:build en 2020-2021.
Hay otros; eso es solo lo más destacado.
Puede pensar que //go: significa #pragma de C si ayuda.

En este punto, la convención está muy bien establecida.
Elegimos esa sintaxis en 2012 porque
(1) obviamente no es un comentario para una persona;
(2) las herramientas que no conocen los comentarios los ignorarán porque son comentarios; y
(3) se generaliza a otras herramientas (s / go / yourtool /).

Y como dijo Ian, # 37974 formalizó la sintaxis exacta de comentarios generalizados, por lo que vale.

Según la discusión anterior, esto parece una aceptación probable .
(Nuevamente, asumiendo pero separado de la propuesta de FS).

Sin cambios en el consenso, así que aceptado.

Estoy ansioso por tener en mis manos la inserción: ¿ya se puede probar en el maestro o hay algún plan para enviarlo como experimento durante el ciclo 1.15?

@andig , Go 1.15 ya está disponible. Todavía espero que esto esté en Go 1.16 y aterrice en la rama de desarrollo este mes.

@rsc 1.16 disponible?

@septs , no, todavía estamos trabajando en Go 1.16. La congelación del código es el 31 de octubre, con una fecha de lanzamiento prevista para el 1 de febrero.

lanzamiento más rápido en 2021Q1 o 2021Q2?

@septs , deje de hacer preguntas sobre los lanzamientos de Go en este hilo. Más de veinte personas lo siguen y reciben notificaciones. Consulte https://golang.org/wiki/Questions y https://github.com/golang/go/wiki/Go-Release-Cycle.

El cambio https://golang.org/cl/243941 menciona este problema: go/build: recognize and report //go:embed lines

El cambio https://golang.org/cl/243940 menciona este problema: go/build: refactor per-file info & reader

El cambio https://golang.org/cl/243942 menciona este problema: embed: implement Files

El cambio https://golang.org/cl/243944 menciona este problema: cmd/compile: add //go:embed support

El cambio https://golang.org/cl/243945 menciona este problema: cmd/go: add //go:embed support

Un detalle que surgió en la revisión de la implementación es que "Archivos" como sustantivo singular es bastante incómodo ("Un archivo contiene ...").

La elección de embed.Files para el nombre es anterior tanto a la propuesta io / fs como al soporte para cadena y [] byte.
Dados estos dos desarrollos, una forma aparentemente sensata de resolver el problema de "Los archivos se mantienen" es llamarlo FS en lugar de Archivos.

Entonces, las tres formas de incrustar e imprimir datos son:

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

Eso parece más claro acerca de lo que obtiene: una cadena, un byte [] o un FS.
Es decir, la mayor parte de la funcionalidad de embed.F * proviene de que es un fs.FS, y llamarlo FS lo hace más claro que llamarlo Archivos.

Hice ese cambio en mi último borrador de la incorporación del paquete de implementación de CL, pero quería volver aquí y ver si hay alguna objeción al cambio de nombre.

(Un cambio más radical sería hacer var f fs.FS lugar de var f embed.FS , pero eso excluiría tener cualquier método en f no sea Open. Por ejemplo, arriba, tener ReadFile es conveniente y no sería posible. Y, en general, hemos aprendido que usar un tipo concreto para algo que podría querer agregar métodos más adelante es una buena prueba de futuro en comparación con usar un tipo de interfaz directamente).

Creo que el cambio de nombre es un buen cambio.

En cuanto al cambio más radical:

  • Si usáramos fs.FS , ¿necesitaríamos más el paquete embed ? Supongo que el valor dinámico todavía debe tener algún tipo, ¿que vive en algún paquete? Considero que la idea de no tener que agregar un paquete es una ventaja.
  • No encuentro muy convincente "no podemos agregar métodos", porque IMO f.ReadFile(…) no es mucho menos conveniente que fs.ReadFile(f, …) .
  • Sin embargo, estoy de acuerdo en que los tipos concretos son mejores en general, por lo que es una ventaja mantenerlo embed.FS
  • Otra pregunta: ¿ embed.FS utiliza receptores de puntero o receptores de valor? En mi opinión, tener que pasar alrededor de &f es incómodo, el uso de receptores de valor es un poco inesperado. Sin embargo, también podríamos permitir var f *embed.FS . Si la variable tiene un tipo de interfaz, esta pregunta desaparece.

En general, sigo estando de acuerdo en que usar el embed.FS concreto es mejor, si no es nada más, entonces para fines de documentación.

Ahora que lo ha mencionado, no creo que tenga esto claro: podemos incrustar directorios, ¿verdad?

Sí, como un embed.FS que implementa fs.FS.

@Merovius , embed.FS usa receptores de valor. embed.FS es una estructura de una palabra que contiene un solo puntero, por lo que no hay una sobrecarga real para hacerlo, pero significa que puede asignarlos y usarlos sin preocuparse por * sy \ & s en todas partes.

@ chabad360 , sí, puede incrustar directorios.

¿Qué pasa con los enlaces simbólicos?

@ burik666 , consulte https://golang.org/s/draft-embed-design para obtener más detalles, pero no, no puede incrustar un enlace simbólico.

¿Será posible incrustar y utilizar bibliotecas C dinámicas? si es así, ¿cómo usaríamos la ruta de inserción en #cgo encabezados como: #cgo LDFLAGS: -L./lib -lmylib -Wl,-rpath=./lib ?

@benitogf Asumo que la única forma real de hacer esto sería escribirlos en el disco y usar dlopen . No puedo imaginar cómo se le puede decir al cargador dinámico cómo encontrar archivos incrustados. Además, si desea agrupar en código C, el enlace estático parecería más apropiado de todos modos, ¿no?

@benitogf Embedding le permite poner un archivo del disco en un byte [] en su programa convenientemente, nada más.
Si tiene una forma de usar una biblioteca C dinámica que ya está en su programa en forma de un byte [], entonces la incrustación lo ayudará a obtener un archivo de disco allí. De otra manera no.

el enlace estático parecería más apropiado de todos modos, ¿no?

@Merovius estuvo de acuerdo, pero tengo varios casos de uso trabajando con proveedores que solo proporcionarán bibliotecas dinámicas

Si tiene una forma de usar una biblioteca C dinámica que ya está en su programa en forma de un byte []
la única forma real de hacer esto sería escribirlos en el disco y usar dlopen

escribir la biblioteca incrustada desde el byte [] al sistema de archivos y usar dlopen parece estar bien, aunque tener los archivos incrustados opcionalmente "volcados" al sistema de archivos en la construcción / ejecución para que el encabezado #cgo pueda acceder a ellos sería útil, no solo para cgo en mi humilde opinión

Probando esto ahora; una verruga con la directiva go:embed es que si incrusto build/* , los nombres de archivo todavía tienen el prefijo build/ . Si luego quiero servir ese directorio a través de http.FS , no hay una manera fácil de _agregar_ el prefijo que se requiere para acceder a ellos si es necesario (sin escribir una envoltura, lo que luego plantea el problema de tener que enumerar todos los métodos potenciales que el FS pueda tener ...).

p.ej:

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

Sé que la intención es que uno pueda escribir go:embed foo/* bar/* baz.ext y obtener todos esos archivos, pero creo que será muy común simplemente incrustar un directorio y servirlo como activos estáticos a través del paquete http. Espero que esto sea un problema ya que la gente cambia de cosas como http.Dir("static") o pkger.Dir("/internal/web/static") donde el prefijo ya se maneja, al nuevo embed.FS .

No estoy muy seguro de cómo presentar esto, ya que es una especie de interacción con embed , io/fs y net/http .

@zikaeroh Escribir un http.Handler también funcionaría allí, ¿verdad? Ese es solo un método e incluso hay http.HandlerFunc . Quizás la biblioteca estándar podría incluso proporcionar una para reflejar http.StripPrefix (algo así como http.AddPrefix o http.ReplacePrefix ).

Potencialmente, aunque se siente un poco extraño modificar la solicitud HTTP para evitar la implementación de FS (a diferencia de un generalizado "dame un FS que sea un subdirectorio de otro FS", que no es simple con métodos opcionales). No sería lo más eficiente, eliminar y luego agregar otro prefijo nuevamente (dadas http.Request copias), pero lo intentaré más tarde. Al menos no es _diferente_ al esquema actual de cosas en las que tienes que trabajar con la solicitud, supongo.

Tengo algunos otros lugares en los que uso datos estáticos, no a través del paquete http, para los que tendré que encontrar una solución similar.

Si tengo que ver cómo se está implementando, ¿dónde puedo mirar? ¿Una sucursal donde se está implementando?

Se sugirió antes incrustar los archivos en el lugar, es decir, hacerlo en el directorio de compilación y luego importarlo. Eso quitaría el prefijo de compilación. Luego use el controlador para agregar el prefijo requerido. No estoy seguro de cómo excluir el archivo go que realiza la incrustación de la incrustación. Ver https://github.com/golang/go/issues/41191#issuecomment -686621090

Se sugirió antes incrustar los archivos en el lugar, es decir, hacerlo en el directorio de compilación y luego importarlo. Eso quitaría el prefijo de compilación. Luego use el controlador para agregar el prefijo requerido. No estoy seguro de cómo excluir el archivo go que realiza la incrustación de la incrustación. Ver # 41191 (comentario)

Desafortunadamente, eso no es bueno para los directorios producidos por otras herramientas, por ejemplo, la salida de, por ejemplo, una compilación de paquete web o CRA (donde a menudo se limpian de antemano y no se registran). Prefiero piratear los nombres de archivo.

Se sugirió antes incrustar los archivos en el lugar, es decir, hacerlo en el directorio de compilación y luego importarlo. Eso quitaría el prefijo de compilación. Luego use el controlador para agregar el prefijo requerido. No estoy seguro de cómo excluir el archivo go que realiza la incrustación de la incrustación. Ver # 41191 (comentario)

Desafortunadamente, eso no es bueno para los directorios producidos por otras herramientas, por ejemplo, la salida de, por ejemplo, una compilación de paquete web o CRA (donde a menudo se limpian de antemano y no se registran). Prefiero piratear los nombres de archivo.

Si utiliza un sistema de complementos tan grande como el paquete web, es fácil instalar otro complemento de paquete web para generar embed.go junto con los propios activos. Si está utilizando algo más simple con un archivo MAKE o un script de shell, también es fácil generar el archivo .go desde allí.

@zikaeroh

a diferencia de un "dame un FS que sea un subdirectorio de otro FS" generalizado, que no es simple con métodos opcionales

Se supone que es simple. Manejar el problema de la envoltura fue parte del proceso de diseño. En particular, se supone que el contenedor implementa todos los métodos opcionales, simplemente llamando a la función auxiliar apropiada en fs . Si eso no funciona, es preocupante y sería genial obtener algunos detalles.

Además, en mi opinión, io/fs (análoga a io.LimitWriter , etc.) debería proporcionar una implementación de dicho contenedor (que elimina un prefijo). Asumiría que la única razón por la que aún no ha sucedido es el tiempo.

@andig El problema con hacer eso es que el archivo Go que contiene la directiva embed y la variable también es visible desde el FS (y sería servido por HTTP o podría quedar expuesto de otra manera).

El problema de hacer eso es que el archivo Go que contiene la directiva embed y la variable también es visible desde el FS (y sería servido por HTTP o podría quedar expuesto de otra manera).

Una forma de remediar eso podría ser agregar la capacidad de excluir archivos / carpetas específicos de la incrustación (¿ @rsc ?)

Una forma de remediar eso podría ser agregar la capacidad de excluir archivos / carpetas específicos de la incrustación (¿ @rsc ?)

La propuesta fue aceptada hace más de un mes y ya está implementada; No creo que los grandes cambios de diseño, como la posibilidad de excluir rutas, sean razonables en este momento. Si tiene un problema con el diseño implementado que no puede solucionar, le sugiero que presente un informe de error por separado con detalles, que luego se puede rastrear antes de la versión final 1.16.

@Merovio

Se supone que es simple. Manejar el problema de la envoltura fue parte del proceso de diseño. En particular, se supone que el contenedor implementa todos los métodos opcionales, simplemente llamando a la función auxiliar apropiada en fs. Si eso no funciona, es preocupante y sería genial obtener algunos detalles.

¿Cómo funcionaría la eliminación de prefijos para Glob ?

@icholy asumo algo como

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
}

Quizás con algún cuidado adicional.

Jugando con esto en gotip, noto que incluirá archivos .DS_Store. Esto es prácticamente ineludible, supongo, pero me preocupa que la inclusión de archivos dot lleve a la inclusión accidental de archivos. ¿Quizás los médicos deberían tener una fuerte advertencia sobre esto?

Mi shell no incluye archivos de puntos en * , así que si quiero incluirlos, tengo que usar * .* . Esta podría ser una forma (quizás igualmente sorprendente) de dar un nivel de control.

No estoy seguro de qué pensar: en mi opinión, los archivos de puntos no deberían ser tratados de manera diferente por el patrón, pero OTOH, el ejemplo .DS_Store parece algo que realmente debería abordarse.

¿Por qué no hacer git clean -dffx && go build ? Si los archivos DS_Store están en git, se incluirán en la compilación. Si no lo son, no lo serán. Esto también funcionará bien con gitignore.

De todos modos, debería estar construyendo con una caja de VCS limpia. Si agrega archivos temporales aleatorios, es posible que lleguen a la compilación final y no es posible que sepa con qué archivos terminará la gente. Es posible que queramos documentar esto.

@mvdan Estoy de acuerdo en principio, pero en la práctica, me preocupa que mucha gente se queme con construcciones sucias si no se les advierte. No quiero ver una filtración secreta de alguien que no se dio cuenta de que su archivo .env estaba incrustado por error. He visto toneladas de ejemplos de un error equivalente con el alojamiento PHP, aunque podría haberse evitado fácilmente diciéndole a Apache que excluyera los archivos de puntos.


Re: incrustación de http.FileServers

Si utiliza un sistema de complementos tan grande como el paquete web, es fácil instalar otro complemento de paquete web para generar embed.go junto con los propios activos.

Eso es cierto, pero es muy complicado. El objetivo de embed.FS es reducir la necesidad de soluciones locas de Makefile. Creo que la solución simple es tener fs.WithPrefix(string) fs.FS que bloquee un FS en un subdirectorio. Pensé que había algo de discusión sobre esto en la propuesta, pero no puedo encontrarlo ahora. Tal vez solo fue discutido en Reddit o algo así.

Una forma de remediar eso podría ser agregar la capacidad de excluir archivos / carpetas específicos de la incrustación (¿ @rsc ?)

Podría ser algo como

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

La directiva embed-exclude podría simplemente hacer un filtro global en los archivos aceptados y eliminar cualquier coincidencia ...

Si queremos evitar agregar más a esta propuesta, también podría ser una regla de pelusa que verifique los sistemas de archivos incrustados en busca de archivos de puntos potencialmente inesperados y le advierta para que pueda corregir su compilación para eliminarlos.

O excluya los archivos de puntos de forma predeterminada a menos que se mencionen específicamente agregando. * O similar.

Eso todavía no se ocuparía de exponer un archivo assets.go. En cuanto a la propuesta que ya se ha implementado, tenga en cuenta que la pregunta sobre la generación de activos se planteó durante la fase de discusión. Probablemente no sea peligroso tener un assets.go expuesto (de lo contrario vacío, a excepción de la directiva de inserción), pero sería más limpio no tenerlo. Como es habitual, hay todo tipo de soluciones alternativas que se pueden aplicar.

Probablemente no sea peligroso tener un assets.go expuesto (de lo contrario vacío, a excepción de la directiva de inserción), pero sería más limpio no tenerlo.

Estoy de acuerdo en que es muy poco probable que esto sea un problema, pero odiaría ver que cualquier código fuente cerrado se filtre accidentalmente debido a una mala configuración si podemos facilitar la configuración correcta.

No quiero ver una filtración secreta de alguien que no se dio cuenta de que su archivo .env estaba incrustado por error.

Si alguien usa //go:embed static/* y hay un static/.env o static/.super-secret , ¿no diría que el usuario realmente quiso incluir esos archivos? De lo contrario, ¿por qué estarían en el directorio estático?

Entiendo que esto depende de lo que el usuario espera y lo que * significa en la mayoría de los contextos, pero personalmente creo que https://golang.org/pkg/path/filepath/#Glob semántica es nuestro único bien opción. Es el más simple y lo que la mayoría de los usuarios de Go estarán acostumbrados en el contexto del desarrollo de Go.

Sin embargo, creo que advertir sobre los peligros de incrustar * es una buena idea en cualquier caso, porque en una cantidad significativa de casos uno podría reducir la posibilidad de error utilizando globs más específicos como *.png .

Además, le animo a que presente un problema por separado que se pueda rastrear con respecto a la versión 1.16, escrito desde el punto de vista de un informe de error. Esta propuesta es aceptada e implementada, por lo que imagino que se cerrará muy pronto. Por ejemplo, podría formular el informe de error como: la compatibilidad con archivos incrustados conduce fácilmente a la inclusión de archivos no deseados (y proporcione algunos ejemplos).

Si alguien usa // go: embed static / * y hay un static / .env o static / .super-secret, ¿no diría que el usuario realmente quería incluir esos archivos? De lo contrario, ¿por qué estarían en el directorio estático?

Hay una gran cantidad de casos de esquina, por ejemplo, abrió una sesión de edición con vim, pero no la cerró, y creó .*.swp archivo

Moviendo la discusión a # 42321.

Esto sería muy apreciado por el equipo de Prisma para nuestro cliente Database Go , ya que usamos un motor de consulta en tiempo de ejecución que está escrito en rust y debe estar en el binario construido de go de alguna manera.

La forma en que lo estamos haciendo actualmente es empaquetar el binario en un archivo .go en go: generate time, pero el tamaño del archivo es mucho mayor que cuando es un archivo .gz binario.

Las incrustaciones nativas harían esto mucho mejor para que pudiéramos incrustar directamente un archivo .gz en el binario go final.

Si alguien usa //go:embed static/* y hay un static/.env o static/.super-secret , ¿no diría que el usuario realmente quiso incluir esos archivos?

No, yo no lo haría.

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

Vea mi comentario posterior en https://github.com/golang/go/issues/42328#issuecomment -720169922.

Me encanta la idea de hacer que los archivos / plantillas estáticos se puedan incrustar, lo que definitivamente puede aumentar mucho la productividad de los desarrolladores.

Pero, ¿deberíamos innovar con otra etiqueta (por ejemplo, @ o lo que sea) que no sea reutilizar este // , que se supone que son comentarios?

A partir de ahora, el // se ha usado en exceso, supongo, piense en estos:

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

Pero, ¿deberíamos innovar con otra etiqueta (por ejemplo, @ o lo que sea) que no sea reutilizar este // , que se supone que son comentarios?

Esa es una discusión separada y aunque sería bueno que las directivas no fueran comentarios, una gran cantidad de código de compatibilidad de go1 ya se basa en eso, por lo que lo más probable es que no cambie.

Pero, ¿deberíamos innovar con otra etiqueta (por ejemplo, @ o lo que sea) que no sea reutilizar este // , que se supone que son comentarios?

Intenté encontrarlo y fallé, pero recuerdo que hubo una decisión de estandarizar en //go:<word> para este tipo de directivas.
No parece una buena idea cambiar la sintaxis, dada la decisión de converger expresamente en ellos.
(Además, por supuesto, son comentarios específicamente para que el compilador los ignore; estas directivas son específicas de la herramienta go, por lo que no deberían filtrarse en el lenguaje adecuado)

Vi una mención sobre el problema io/fs que embed.FS admite ETag: https://github.com/golang/go/issues/41190#issuecomment -737702433

Intenté ejecutar una prueba, pero no veo un conjunto de encabezado de respuesta ETag . Quizás estoy malinterpretando el uso. ¿Debería esperar ver uno aquí? https://play.golang.org/p/Wq5xU5blLUe

No creo que lo haga. http.ServeContent (usado por http.FileServer ) inspecciona el encabezado ETag, pero no lo establece, AIUI.

En un comentario anterior, Russ ha dicho que ETag se hará funcionar . La dificultad es cómo hacer que embed.FS comunique con http.FileServer la información necesaria para configurar ETag u otros encabezados de almacenamiento en caché. Probablemente debería haber un problema de seguimiento por separado para esto.

Personalmente, diría que embed.FS debería usar el tiempo de la última confirmación del módulo relevante como ModTime . Esto correspondería aproximadamente a debug.BuildInfo , por lo que no afectaría la reproducibilidad. Aunque no estoy seguro de cómo se configura eso para las confirmaciones que no corresponden a una versión etiquetada y aún quedaría la pregunta de en qué configurarlo para las compilaciones a partir de árboles de trabajo sucio.

Pero, confío en que @rsc tenga una buena solución en mente :)

No estoy seguro de entender el comentario sobre "comprometer tiempo"; si la fuente del módulo es un archivo zip, no hay "confirmación". No veo ninguna hora mencionada en debug.BuildInfo o debug.Module .

Más importante aún, diría que cualquier mecanismo basado en marcas de tiempo es estrictamente inferior a un etag adecuado (basado en hash de contenido).

@ tv42 Cada versión del módulo es a) una versión semántica derivada de una etiqueta (que apunta a una confirmación) ob) una pseudo-versión que contiene un hash de confirmación. ¿Creo? Al menos en git. Puede que esté malinterpretando algo.

Más importante aún, diría que cualquier mecanismo basado en marcas de tiempo es estrictamente inferior a un etag adecuado (basado en hash de contenido).

No estoy muy seguro. O necesita algún canal lateral para comunicar el hash, o el servidor necesita calcular el hash del archivo a pedido (lo que parece bastante caro). Después de todo, net/http no sabe, a priori, si el contenido de fs.FS puede cambiar. El resultado final de una ETag basada en hash podría justificar el costo de agregar dicho canal lateral (como una interfaz opcional), pero obviamente no parece estrictamente mejor.

Además, yo diría que apoyar al menos también un enfoque basado en el tiempo significaría que puede trabajar con más clientes. Sin embargo, no tengo ningún dato que respalde eso (es decir, no sé si hay ni cuántos clientes hay que admitan If-Modified-Since pero no ETag y viceversa).

Pero realmente, no me importa mucho qué enfoque se elija. Solo quería mencionar la opción de usar la hora en que se etiquetó una versión del módulo.

Los módulos de

$ 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

Parece haber una marca de tiempo en el archivo *.info adjunto, pero no tengo idea de si es confiable o está disponible para las herramientas.

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

Incluso entonces, ¿qué marca de tiempo debería ir: uso incrustado en el módulo principal (el que ejecuta go build en)?

Personalmente, en el contexto de archivos estáticos que están incrustados de manera inmutable en el binario, el hash parece superior a las marcas de tiempo en todos los aspectos (excepto el soporte heredado de antes de que existiera ETag, antes de HTTP / 1.1, los años 90), y no tiene una fuente posible de confusión en sistemas distribuidos y compilaciones simultáneas. Dudo seriamente que existan clientes que ya no entienden ETag, y seguro que no harán la transición HTTP / 2.

Un canal lateral para comunicar el hash me parece lo correcto; una interfaz FS opcional para devolver un hash del archivo. (¿Y tal vez otro para solicitar un algoritmo hash específico, si es que lo tienen ?. Algunos casos de uso de servicio realmente necesitan específicamente sha256 / sha1 / md5, no solo "algo de hash"). Un FS que no tiene una respuesta lo suficientemente barata puede optar por no implementarla.

(Aunque los hashes modernos son gigabytes / segundo / núcleo incluso cuando son criptográficamente seguros, decenas de gigabytes / segundo para los menos seguros y fáciles de almacenar en caché según la llamada estadística. Solo admite ETag en todas partes, gente).

Hoy he estado pensando en esta propuesta. Estoy feliz de ver esta funcionalidad incluida en la cadena de herramientas de Go, y agradezco todo el pensamiento y el esfuerzo que se ha dedicado a la propuesta hasta la fecha. Gracias por proponer esto e impulsarlo.

Tengo dos preguntas, y luego una sugerencia (aprendo que esto ya pasó del período de discusión de la propuesta y ya está congelado):

Tipos permitidos

Noto que el código actual espera que las variables se declaren con precisión ~ embed.FS ~, string , o []byte . En particular, estos no están permitidos: []uint8 , ~ FS después de la importación de puntos de "embed" ~, o cualquier otro tipo idéntico construido usando alias de tipo. (Editar: olvidé cómo funcionan las importaciones de puntos dentro de gc y leí mal el código para detectar embed.FS ).

¿Es esto intencionado o un error?

Semántica de variables de tipo []byte .

No veo ninguna mención de cuál es la semántica de identidad esperada de las variables de tipo []byte . En particular, para variables de ámbito de función. Esto no es importante para las variables de tipo string y embed.FS , porque hacen referencia a datos inmutables que se pueden deduplicar de forma segura. Pero es importante conocer la semántica prevista para las variables de tipo []byte .

Con la implementación actual, el programa de prueba a continuación imprime false true (cuando foo.txt no está vacío). ¿Eso está previsto / garantizado?

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

Creo que la semántica más parecida a Go para las variables //go:embed serían las de los literales compuestos: que cada ejecución produce una nueva copia.

Si no hay consenso sobre la semántica adecuada para esto, siempre podemos apuntar y hacer que []byte incruste un error para Go 1.16: los usuarios aún pueden declarar una variable a nivel de paquete si quiere la semántica actual (un segmento de byte por declaración de origen), o use una variable de tipo string conviértala a []byte si quieren semántica de literal compuesto. Luego podríamos revisar más tarde qué comportamiento los usuarios se beneficiarían más.

Evitar más directivas //go:

Recomiendo no agregar directivas //go: para usuarios finales que afecten la semántica del programa, y ​​no encuentro convincentes los argumentos dados para favorecer //go:embed sobre la sintaxis Go normal. Recomiendo respetuosamente que reconsidere esta decisión antes del lanzamiento de Go 1.16. (Nuevamente, agradezco lo tarde que es esta solicitud).

Comenzaré señalando que tengo una prueba de concepto CL en funcionamiento en inserción de:

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

para

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

De manera similar, las funciones embed.Bytes y embed.String están disponibles para incrustar un solo archivo y obtenerlo como un []byte o string .

Respuestas

De manera similar, una variable embed.Files puede ser una variable global o local, dependiendo de lo que sea más conveniente en el contexto.

Tener embed.Files , etc. también permite usarlos en contextos de expresión, lo que puede ser aún más conveniente.

Es un error usar // go: embed en un archivo fuente que no importa "embed" (la única forma de violar esta regla implica el engaño de los alias de tipo).

Con embed.Files , etc., esto es un error debido a que las funciones están siendo exportadas por el paquete incrustado.

A goimports (y gopls, etc.) se les puede enseñar esta regla y agregar automáticamente la importación en cualquier archivo con un // go: embed según sea necesario.

No se necesita ninguna lógica de goimports o gopls especial para saber que la importación de "incrustar" es la forma correcta de corregir los usos de embed.Files , etc.

Este enfoque soluciona el problema de la verificación de tipos, no es un cambio de idioma completo, pero aún tiene una complejidad de implementación significativa.

En particular, CL 276835 es una eliminación neta de código. En particular, el código del compilador (que tendrá que volver a implementarse en gccgo y otros compiladores) es mucho más simple.

También espero que sea más fácil enseñar a go / types sobre la semántica matizada de embed.Files , etc. (es decir, que solo aceptan argumentos literales de cadena), que enseñarle sobre //go:embed .

El comando go necesitaría analizar todo el archivo fuente de Go para comprender qué archivos deben estar disponibles para la incrustación. Hoy solo analiza el bloque de importación, nunca las expresiones Go completas

Con CL 276835, el comportamiento es el mismo que en tip: el comando go analiza todo el archivo fuente de Go para los archivos que importan el paquete incrustado, y solo las importaciones para los archivos que no lo hacen.

Es cierto que para los archivos que se importan incrustados, CL 276835 realiza un análisis y un recorrido completos, mientras que tip hace un análisis de cadenas más eficiente para los comentarios de //go:embed . Creo que un algoritmo de un solo paso más optimizado para encontrar llamadas embed.Files es factible si se desea.

Tampoco estaría claro para los usuarios qué restricciones se imponen a los argumentos de estas llamadas especiales: se ven como llamadas Go ordinarias pero solo pueden tomar cadenas literales, no cadenas calculadas por código Go, y probablemente ni siquiera constantes nombradas (o el El comando go necesitaría un evaluador de expresiones Go completo).

Esto no me parece sustancialmente diferente de la directiva //go:embed : los usuarios no tienen ninguna expectativa sobre qué argumentos pueden usar allí hasta que lo usan por primera vez. Además, de cualquier manera, los usuarios recibirán mensajes de error del compilador si lo usan incorrectamente, pero los IDE y otras herramientas proporcionarán automáticamente mejores godocs y referencias cruzadas por embed.Files , etc.

@mdempsky FS después de una importación de puntos de embed es del mismo tipo. Entonces, ese caso en particular parece un sencillo "sí, está bien" (lo probé y ya funciona). Del mismo modo, byte es un alias para uint8 , por lo que []uint8 también es del mismo tipo que []byte , aunque sorprendentemente, esto no funciona en este momento. . Sin embargo, yo diría que la semántica implementada está bien, por ahora - siempre podemos permitir más tipos y / o alias y / o incluso "el mismo tipo subyacente" más adelante, si surge la necesidad.

Creo que la semántica más parecida a Go para // go: las variables de inserción serían las de los literales compuestos: que cada ejecución produce una nueva copia.

Tiendo a estar de acuerdo, esa era mi expectativa también. Al principio pensé que esto podría haber sido un artefacto de análisis de escape y el compilador se dio cuenta de que no cambia los datos, pero no:

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

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

Sin embargo, "cada declaración de var crea una nueva variable" también tiene sus problemas, porque significa código como este

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

asignaría y copiaría innecesariamente. Quizás eso esté bien y se vea compensado por los beneficios de una semántica más clara. Pero quizás también sea una señal para prohibir las incrustaciones locales de []byte como mencionas.

También espero que sea más fácil enseñar a go / types sobre la semántica matizada de embed.Files, etc. (es decir, que solo aceptan argumentos literales de cadena), que enseñarle sobre // go: embed.

Solo las constantes de cadena al menos, incluso se pueden hacer en el sistema de tipos Go.

Pero, ¿ go/types incluso necesita saber acerca de los archivos incrustados, con //go:embed ? El tipo de esas variables es bastante claro, después de todo (con la excepción de las incrustaciones locales []byte , como se discutió).

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

¿Es esto realmente destinado a ser mutable? No puedo pensar en un solo caso de uso en el que quisiera incrustar un activo estático, pero mutarlo en tiempo de ejecución, así que siento que eso solo habilitará errores. Hubiera sido más feliz metiéndolo en .rodata y entrando en pánico si algo intenta mutarlo.

(Solicitado por x[0] = 'x' arriba).

@Merovius escribió:

@mdempsky FS después de una importación de puntos de embed es del mismo tipo. Entonces, ese caso en particular parece un sencillo "sí, está bien" (lo probé y ya funciona).

Gracias. Olvidé cómo funcionan las importaciones de puntos dentro del compilador, así que leí mal el código. Debería haberlo intentado primero antes de incluir ese ejemplo.

Sin embargo, yo diría que la semántica implementada está bien, por ahora - siempre podemos permitir más tipos y / o alias y / o incluso "el mismo tipo subyacente" más adelante, si surge la necesidad.

[]byte y []uint8 son tipos idénticos bajo la especificación Go, al igual que muchos otros tipos construidos con alias de tipo. Si no es aceptable que un compilador trate embed.Files("foo" + ".txt") diferente a embed.Files("foo.txt") , entonces el mismo argumento debería extenderse para no permitirle tratar los tipos con alias de manera diferente.

Pero, ¿ go/types incluso necesita saber acerca de los archivos incrustados, con //go:embed ?

No, no es necesario, al igual que tampoco es necesario que conozca la semántica especial de embed.Files . Pero para cualquiera de las sintaxis, todavía podría ser útil que go / types advierta sobre el uso indebido.

-

@ tv42 escribió:

¿Es esto realmente destinado a ser mutable?

Evidentemente. El compilador de Go organiza específicamente que las incrustaciones de string y embed.FS se deduplican y se pongan en rodata, mientras que las incrustaciones de []byte no son:

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

Sin embargo, es cierto que la propuesta no parece haber abordado este punto.

Gracias por los atentos comentarios. He presentado dos problemas para dar seguimiento:

# 43216 - Eliminar el soporte para incrustar directivas en variables locales
# 43217 - define los alias de tipo String y Bytes que deben usarse con // go: embed

No presenté un problema para la sintaxis // go: embed en sí. Quería decir por qué aquí, para que a todos no les parezca que estoy descartando eso.

Cuando escribí en un blog sobre el proceso de propuesta, pasé mucho tiempo buscando formas de (y no) manejar las decisiones en grupos grandes. Una fuente que encontré que tenía mucho sentido para mí fue la publicación “ Toma de decisiones abierta ” de John Ousterhout. Vale la pena leer toda la publicación, pero solo citaré aquí la sección sobre Reconsideración:

La última regla para la toma de decisiones abierta es asegurarse de no reconsiderar una decisión a menos que haya información nueva importante. Esta regla tiene dos partes. Primero, debe estar preparado para corregir decisiones que resulten ser incorrectas de manera significativa. Esto es particularmente cierto en las startups: muchas decisiones deben tomarse sin información completa e inevitablemente algunas de ellas resultarán incorrectas. Una decisión incorrecta que se corrige rápidamente causa poco o ningún daño, pero una decisión incorrecta que no se corrige puede ser catastrófica.

Por otro lado, no debe reconsiderar una decisión a menos que haya salido a la luz nueva información significativa desde que se tomó la decisión original. Si no hay nueva información disponible, entonces reconsiderar una decisión probablemente producirá el mismo resultado que la decisión original, haciendo perder el tiempo a todos. No es inusual que los empleados acudan a mí semanas después de que se tomó una decisión: "John, voté en contra de la decisión a XYZ, y cuanto más lo pienso, más convencido estoy de que estaba mal; realmente creo que debemos reconsidere esto ". Mi respuesta es "¿Qué información nueva tienes?" Si la respuesta es "ninguna", no lo reconsideramos. En situaciones como esta, es útil tener un registro de los argumentos presentados durante la discusión, para que pueda verificar que la nueva información realmente es nueva. Si facilita la reapertura de las decisiones, termina vacilando de un lado a otro donde ninguna decisión es definitiva y los empleados dudan en implementar decisiones porque no creen que sean permanentes.

Si desea asegurarse de no reconsiderar muchas decisiones, asegúrese de recopilar opiniones ampliamente durante el proceso de toma de decisiones. Si no obtiene suficientes aportes, aumenta la probabilidad de que surjan nuevos aportes significativos después de la decisión, lo que significa que tendrá que reconsiderarlo. Si hace un buen trabajo al recopilar información, es mucho menos probable que tenga que revisar sus decisiones.

Esto realmente me resonó: el proceso de propuesta de Go (como los grandes proyectos de código abierto en general) es un sistema con una carga ofrecida mucho más alta que la capacidad de trabajo, por lo que es importante que (no estemos de acuerdo si es necesario) nos comprometemos y pasamos a la siguiente decisión. .

La cadena y el byte [] se agregaron bastante tarde en el proceso de considerar este problema, en respuesta a la retroalimentación inicial, y claramente no pensamos en todas las implicaciones de esos. Así que presenté esos dos números nuevos, # 43216 y # 43217.

Por otro lado, la sintaxis // go: embed fue una parte central de las discusiones originales y se discutió ampliamente, tanto los pros como los contras. No creo que haya "información nueva significativa" que deba hacernos reconsiderar esa sintaxis por completo, por lo que en aras de seguir adelante y tener en cuenta el consejo de Ousterhout sobre la reconsideración, lo dejo a un lado.

Gracias de nuevo por señalar los problemas de cadenas y [] bytes.

Evidentemente. El compilador de Go organiza específicamente que las incrustaciones string y embed.FS se deduplican y se pongan en rodata, mientras que las incrustaciones []byte no son:

@rsc , ¿pensaste en este último punto por casualidad? Con la implementación actual, podría convertirse en la "mejor práctica" utilizar cadenas en lugar de [] bytes para producir mejores binarios. ¿Estamos de acuerdo con eso?

No entiendo por qué sería una "mejor práctica". Para mí, eso es similar a decir que es "la mejor práctica" hacer constantes de errores centinela, ergo, no deberíamos permitir variables de tipo error en el ámbito del paquete, ya que no estoy de acuerdo con que sea una buena práctica y con la restricción adicional como un solución.

Pude ver un argumento para usar solo string en vars locales. Pero en las variables de alcance de paquete, la semántica es clara y está bien definida y no recomendaría no usar un []byte incrustado más de lo que recomendaría no usar cualquier otra variable []byte .

@mvdan , si la gente se string lugar de []byte como una mejor práctica, lo consideraría algo bueno. string es el tipo Go apropiado para “una cadena de bytes inmutable”, mientras que []byte es el tipo apropiado para “una cadena de bytes mutable” o “una cadena de bytes de mutabilidad indeterminada”.

Para los casos en los que tiene un valor de tipo string (inmutable) y desea usarlo como tipo []byte (indeterminado), ya puede usar unsafe para hacerlo correctamente . (Ver, por ejemplo, mi unsafeslice.OfString ). Quizás deberíamos agregar una biblioteca estándar compatible para esa operación, pero eso parece una propuesta separada.

Por lo tanto, parece correcto usar siempre el tipo string si realmente desea que el valor sea de solo lectura.

@Merovius @bcmills planteas buenos puntos y, para ser claros, no me opongo. Solo quiero asegurarme de que los diseñadores de propuestas piensen en esta distinción antes del lanzamiento final.

Realmente no creo que la deduplicación vaya a surgir mucho en la práctica. (¿En qué configuración van a incrustar varios paquetes exactamente los mismos archivos?) La gente debería usar el formulario que necesita y no preocuparse por "cadena significa que mis binarios son más pequeños", porque en general no creo que eso sea cierto.

Algunas personas preguntaron sobre ETags. Se nos acabó el tiempo para eso, pero he presentado una propuesta en https://github.com/golang/go/issues/43223 y espero que eso nos lleve a una buena idea que pueda incluirse en Go 1.17. Perdón por no poder conseguirlo en esta ronda.

Gracias por presentar # 43216 y # 43217. Si se aceptan, abordarán sustancialmente mis inquietudes pendientes con la propuesta //go:embed .

Por otro lado, la sintaxis // go: embed fue una parte central de las discusiones originales y se discutió ampliamente, tanto los pros como los contras. No creo que haya "información nueva significativa" que deba hacernos reconsiderar esa sintaxis por completo, por lo que en aras de seguir adelante y tener en cuenta el consejo de Ousterhout sobre la reconsideración, lo dejo a un lado.

Respeto no querer volver a tratar asuntos que ya se han decidido a través de una extensa discusión. Pero después de revisar la discusión que tuvo lugar en # 35950, el hilo de Reddit , y aquí, no creo que justifiquen la decisión de usar //go:embed .

Aquí están los comentarios que encontré que tocaron la sintaxis para indicar qué archivos incrustar:


18 Problema de GitHub y comentarios de Reddit

  • https://github.com/golang/go/issues/35950#issuecomment -561443566 "El enfoque //go:embed introduce otro nivel de complejidad. Tendría que analizar los comentarios mágicos para incluso verificar el tipo código. El enfoque de "paquete embebido" parece más amigable para el análisis estático ". (Nota: la propuesta //go:embed revisada facilita la escritura del código de verificación, pero aún no es trivial si un analizador realmente quisiera ver las cadenas utilizadas, porque no están en go / ast).
  • https://github.com/golang/go/issues/35950#issuecomment -561450136 "Ese es un argumento muy sólido para usar un paquete. También lo hace más legible y documentable, ya que podemos documentarlo todo con un godoc normal, en lugar de que en los documentos de cmd / go ". (Nota: creo que esto se aborda principalmente en # 43217, ya que los tipos de inserción de paquetes proporcionan un punto de referencia fácil).
  • https://github.com/golang/go/issues/35950#issuecomment -561840094 "¿Hay alguna razón por la que no pueda ser parte de go build / link ... por ejemplo, go build -embed example=./path/example.txt y algún paquete que exponga acceso a él (por ejemplo, embed.File("example") , en lugar de usar go:embed ? "(Sugiere la sintaxis de la función sobre la directiva //go: . Voto en contra, pero sospecho que porque sugirió go build banderas.)
  • https://github.com/golang/go/issues/35950#issuecomment -561726107 "No soy particularmente fanático de un comentario que se compila en código, pero también encuentro que el pseudopaquete que afecta la compilación es un poco extraño también. Si no se usa el enfoque de directorio, tal vez tenga un poco más de sentido tener algún tipo de declaración de nivel superior incorporada en el idioma. Funcionaría de manera similar a la importación, pero solo admitiría rutas locales y requeriría un nombre para que se le asigne ". (No le gustan ni //go: ni las nuevas funciones integradas, pero prefiere el código en lugar de los comentarios).
  • https://github.com/golang/go/issues/35950#issuecomment -561856033 "Además, dado que las directivas //go:generate no se ejecutan automáticamente en go build, el comportamiento de go build puede parecer un poco inconsistente: //go:embed funcionará automáticamente, pero por //go:generate tienes que ejecutar go generate manualmente ( //go:generate ya puede interrumpir el flujo de go get si genera los archivos .go necesarios para la compilación). " (Incluido para completar, pero ya tenemos //go: directivas que afectan y no afectan el comportamiento de go build incluso sin //go:embed , por lo que este argumento no me parece convincente).
  • https://github.com/golang/go/issues/35950#issuecomment -562005821 "Votaría por el nuevo paquete en lugar de la directiva. Mucho más fácil de controlar, más fácil de manejar / administrar y mucho más fácil para documentar y ampliar. Por ejemplo, ¿puede encontrar fácilmente la documentación para una directiva de Go como “go: generate”? ¿Qué pasa con la documentación del paquete “fmt”? ¿Ve a dónde voy con esto? " (Nuevamente, abordado principalmente por # 43217).
  • https://github.com/golang/go/issues/35950#issuecomment -562200553 "Un argumento a favor de un comentario pragma (// go: embed) en lugar de un nombre de directorio especial (estático /): un comentario nos permite incrustar un archivo en el archivo de prueba para un paquete (o el archivo xtest) pero no la biblioteca bajo prueba. El comentario solo debe aparecer en un archivo _test.go ". (Nota: el argumento está en contra de un directorio especial, no específicamente para //go:embed ; el mismo argumento se extiende a las funciones embed.Files , etc.)
  • https://github.com/golang/go/issues/35950#issuecomment -562235108 "Me gusta el enfoque package embed porque usa la sintaxis Go"
  • https://github.com/golang/go/issues/35950#issuecomment -562713786 "El enfoque import "C" ya ha sentado un precedente para las rutas de importación" mágicas ". En mi opinión, ha funcionado bastante bien".
  • https://github.com/golang/go/issues/35950#issuecomment -562768318 "Varias personas han hablado de proporcionar automáticamente una API para leer los archivos incrustados, en lugar de necesitar otra señal como un comentario mágico. Creo que debería ser el camino a seguir, ya que ofrece una sintaxis programática familiar para el enfoque. Optar por un paquete especial, posiblemente runtime/embed como se mencionó anteriormente, satisfaría eso y permitiría una fácil extensibilidad en el futuro ".
  • https://github.com/golang/go/issues/35950#issuecomment -562959330 "En lugar de reutilizar los comentarios (que terminan esparcidos por todas partes y, en una nota personal, simplemente se sienten asquerosos), [... ]. " (Continúa sugiriendo el uso de archivos go.mod para enumerar archivos incrustados. Para ser justos, también argumenta "Esto soluciona problemas como tener que restringir el paquete mágico para permitir solo cadenas literales como argumentos, pero significa que es más difícil verificar si los activos incorporados se utilizan en cualquier lugar o terminan siendo un peso muerto ").
  • https://github.com/golang/go/issues/35950#issuecomment -562966654 "Otra idea más: en lugar de agregar un nuevo tipo de verbo embed en go.mod, podríamos introducir un nuevo tipo de paquete, un paquete de datos , que se importa y se utiliza en go.mod de la forma habitual. Aquí hay un boceto de hombre de paja ". (Preferencia por el código sobre los comentarios).
  • https://github.com/golang/go/issues/35950#issuecomment -563156010 "Aquí hay algo que aún no se ha mencionado: la API para las herramientas de procesamiento de código fuente Go (compilador, analizadores estáticos) es tan importante como la API en tiempo de ejecución. . Este tipo de API es un valor fundamental de Go que ayuda a hacer crecer el ecosistema (como go/ast / go/format y go mod edit ). [...] En el caso de un paquete especial, no veo nada que cambiar en go.mod análisis ( go mod herramientas) o go/ast analizador ".
  • https://github.com/golang/go/issues/35950#issuecomment -601748774 Sugiere una lista de archivos de recursos de go.res. Utiliza código, no comentarios.
  • https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fyv9gxw/ "¿Por qué necesitamos el comentario // go: embed, por ejemplo, binclude hace binclude.Include(filename) para incluir un archivo / directorio que sobre fs.Embed(filename) ? "
  • https://github.com/golang/go/issues/41191#issuecomment -686625127 "En mi opinión, agregar azúcar sintáctico en el idioma para respaldar este cambio de herramientas es un cambio de idioma. Estoy seguro de que esto es obvio para los demás, pero esto es efectivamente un comentario como código. Creo firmemente que la magia / azúcar resta valor a la simplicidad y legibilidad del lenguaje; es muy fácil pasar por alto un comentario mágico que incrusta un archivo ".
  • https://github.com/golang/go/issues/41191#issuecomment -690423900 "Tengo preocupaciones similares a las de @tristanfisher . Go ha estado usando comentarios mágicos como directivas del compilador durante mucho tiempo (¿desde el principio?) están pensados ​​para casos de esquina y es raro que aparezcan en el código. Dada la popularidad de incrustar contenido estático en los binarios de Go, es probable que //go:embed sea ​​más común. Tal vez sea hora de considerar una sintaxis diferente para el compilador directivas? "
  • https://github.com/golang/go/issues/41191#issuecomment -690522509 "Comparto sus preocupaciones sobre los comentarios mágicos".

Cuando leo los comentarios, parecen estar siempre a favor de la sintaxis de Go, con la salvedad de querer evitar cambios que rompan las herramientas. No vi a nadie discutiendo por //go:embed como la ortografía, tanto como simplemente aceptarlo como está. Agregar nuevos elementos intrínsecos del compilador con códigos auxiliares de compatibilidad con versiones anteriores para verificadores de tipo heredados parece más en línea con la preferencia expresada por los participantes de la discusión que más directivas //go: .

Al estilo de las encuestas de pulgar hacia arriba / pulgar hacia abajo en # 43216 y # 43217:

  • Aprobado (👍) si prefiere nuevos elementos intrínsecos del compilador (por ejemplo, CL 276835 ):

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

    Consulte embed_test.go para obtener más ejemplos de uso.

    (Nota: Los argumentos deben ser literales de cadena , no simplemente valores de cadena. Es decir, constantes de cadena declaradas como const logo = "logo.jpg" o expresiones de cadena constante como "logo" + ".jpg" tampoco están permitidas. Sin embargo, embed.Files , etc. pueden usarse en cualquier contexto, no solo para inicializar variables).

  • Pulgar hacia abajo (👎) si prefiere nuevas directivas del compilador (es decir, la propuesta actual):

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

@mdempsky Siento que Russ ha sido bastante claro, lo que se necesitaría para justificarle una revocación de la decisión: nueva información. Creo que recopilar comentarios anteriores claramente no es eso. Sin intención de ofender.

No hay precedentes para estos nuevos tipos de funciones, ¿verdad? Es decir, ¿algo que parece una función de paquete normal pero que en realidad es un tipo especial de función incorporada que solo se puede llamar de cierta manera?

Dice "intrínseco" pero los intrínsecos actuales se comportan exactamente como las funciones normales de Go.

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

Disfrazar nuevas directivas para que se parezcan a las funciones de Go pero con una sintaxis diferente (más restrictiva) a la de las funciones de Go parece una mala opción desde donde estoy sentado. Creo que embed debería describirse en la especificación, por un lado, a diferencia de las directivas //go: .

(Puede aproximar los "únicos argumentos literales permitidos" en el código Go normal haciendo una función que tome argumentos type internalString string no exportados, pero deduzco que eso no es lo que está proponiendo ya que su CL hace cambios en el analizador y comprobador de tipos.)

@Merovius Según el proceso de propuesta de Go:

Consenso y desacuerdo

El objetivo del proceso de propuesta es llegar a un consenso general sobre el resultado de manera oportuna.

Si no se puede llegar a un consenso general, el grupo de revisión de la propuesta decide el siguiente paso al revisar y discutir el tema y llegar a un consenso entre ellos. Si ni siquiera se puede llegar a un consenso entre el grupo de revisión de la propuesta (lo que sería extremadamente inusual), el árbitro (rsc @) revisa la discusión y decide el siguiente paso.

En mi lectura de los comentarios, el consenso pareció favorecer la sintaxis del código Go. Si el consenso ahora es de hecho seguir con //go:embed , lo respeto. Pero no creo que el proceso documentado justificara la decisión inicial de seguir adelante con //go:embed .

(Por el momento, los resultados de la encuesta favorecen débilmente las nuevas funciones sobre las nuevas directivas, pero no por mucho. Si los pulgares arriba no superan en número a los negativos al menos 2: 1, estoy de acuerdo con simplemente descartar esto).

@cespare

No hay precedentes para estos nuevos tipos de funciones, ¿verdad? Es decir, ¿algo que parece una función de paquete normal pero que en realidad es un tipo especial de función incorporada que solo se puede llamar de cierta manera?

Existen tanto las funciones predeclaradas del universo como las funciones no seguras del paquete.

Puede argumentar que el paquete inseguro está documentado en la especificación Go, pero yo sostengo que no es necesario. Go utilizado para admitir modos en los que el paquete no seguro no estaba disponible para los usuarios, e incluso hoy en día, el paquete no seguro tiene funciones y restricciones que solo se documentan en los documentos del paquete, no en las especificaciones de Go.

También hay funciones internas dentro del tiempo de ejecución de Go que son implementadas solo por el compilador de Go. Por ejemplo, getg , getcallerpc , getcallersp y getclosureptr .

(Puede aproximar los "únicos argumentos literales permitidos" en el código Go normal creando una función que tome argumentos de cadena de tipo internalString no exportados,

Creo que sería una adición razonable para reducir aún más el comportamiento de los verificadores de tipo heredado.

pero supongo que eso no es lo que está proponiendo, ya que su CL realiza cambios en el analizador y el comprobador de tipos).

CL 276835 no cambia el analizador, excepto para eliminar el nuevo código del analizador agregado por //go:embed . Cambia el verificador de tipos, pero es comparable a lo que //go:embed hizo antes.

Sería fácil extender go / types para estar al tanto de la inserción de paquetes, pero elegí no hacerlo para CL 276835 específicamente para mostrar que todavía funciona (por ejemplo, cmd / vet no falla en las pruebas unitarias de inserción de paquetes).

@mdempsky Es posible que no esté de acuerdo sobre si se llegó a un consenso en ese momento. Del mismo modo que podría no estar de acuerdo con la decisión en sí. Sin embargo, no creo que eso realmente cambie las cosas. Al final del día, "hay consenso" también es una decisión que se tomó. Y exactamente los mismos puntos sobre la necesidad de nueva información para una revocación se aplican a esa decisión.

Necesitar satisfacer a cada persona es un vector DDoS, tanto en lo que respecta a la decisión como al proceso con el que se tomó.

FTR, se ha discutido la cuestión de las herramientas frente al cambio de idioma, al igual que el compromiso entre "literal de cadena frente a constante de cadena" ("literal de cadena" requiere magia en el verificador de tipos, "constante de cadena" requiere la Ir a la herramienta para realizar la verificación de tipos; los comentarios no necesitan ninguno). Entonces, nuevamente, realmente no hay nada nuevo aquí. Es posible que no esté de acuerdo con el resultado de esa decisión o con el proceso con el que se tomó, pero los argumentos que menciona se han tenido en cuenta al tomar la decisión.

Necesitar satisfacer a cada persona es un vector DDoS, tanto en lo que respecta a la decisión como al proceso con el que se tomó.

No estoy exigiendo estar personalmente satisfecho, y me parece insultante que califique mis publicaciones como tales. Anteriormente también me hablaste como si no estuviera familiarizado con el lenguaje Go o el compilador Go. Por favor deje de ser condescendiente.

Enumeré anteriormente numerosos comentarios en los que la gente expresó casi universalmente su preferencia por no agregar nuevas directivas //go: , mientras que nadie había comentado afirmativamente en apoyo de ellas. Como tal, me pareció que la abrumadora preferencia expresada por la comunidad favorecía la sintaxis de Go, y mi comentario estaba argumentando en defensa de sus preferencias declaradas.

Sin embargo, tal como está, https://github.com/golang/go/issues/41191#issuecomment -747095807 tiene más pulgares hacia abajo que hacia arriba. Eso me sorprende, porque parece inconsistente con todos los comentarios durante la discusión anterior. Pero me complace aceptar que la pregunta se ha abordado directamente y (especialmente como alguien que participará en el soporte a largo plazo para esta función dentro del compilador de Go) ahora estoy más feliz de admitir //go:embed ya que de hecho es la preferencia de la comunidad y no solo la preferencia de los autores de la propuesta. Este resultado no se habría alcanzado si la discusión se hubiera cerrado, como parece estar concentrado.

FTR, se ha discutido la cuestión de las herramientas frente al cambio de idioma, al igual que el compromiso entre "literal de cadena frente a constante de cadena" ("literal de cadena" requiere magia en el verificador de tipos, "constante de cadena" requiere la Ir a la herramienta para realizar la verificación de tipos; los comentarios no necesitan ninguno).

Ese comentario es irrelevante para los argumentos que estaba haciendo. La ortografía alternativa que hice como prototipo en CL 276835 tiene exactamente las mismas propiedades técnicas que la ortografía //go:embed : las herramientas que necesitan saber qué archivos se deben incrustar deberán actualizarse para procesar los archivos de origen de Go de manera diferente (por ejemplo, para error sobre el uso indebido de los comentarios //go:embed o la función incorporada embed.Bytes ), mientras que las herramientas existentes pueden continuar procesando el código razonablemente sin preocuparse por ellos (por ejemplo, go / types ignorará los comentarios //go:embed pero no detectará si se aplica a tipos de variables incorrectos, y puede verificar el tipo embed.Bytes usando las declaraciones stub, pero no sabrá rechazar todas las llamadas que usen argumentos que no sean literales de cadena).

La cuestión de si alguno de estos es un "cambio de lenguaje" es más filosófica que técnica.

(El argumento de Russ de que "si un programa es válido no cambia" en la propuesta //go:embed también es incorrecto. Https://github.com/golang/go/issues/41191#issuecomment-747799509 da un ejemplo de un paquete que era y es válido de acuerdo con la especificación de Go y que también fue aceptado por las versiones de la cadena de herramientas de Go hasta Go 1.15, pero que ya no será válido en Go 1.16).

Como alguien que le dio un 👍 a la propuesta de Matt de usar var website = embed.Files("logo.jpg", "static/*") , mi preocupación con el uso del formulario de comentarios ( //go:embed logo.jpg static/* ) es la "facilidad de uso".

Por ejemplo, estas 2 muestras de programa generarían 2 cosas diferentes, solo porque se omitió una "importación":

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
}

Al obligar al desarrollador a utilizar la importación a través de la semántica del lenguaje, minimiza los problemas en los que los archivos incrustados no funcionan como se esperaba porque no se inicializan como se esperaba.

@ kushieda-minori Si bien creo que mi propuesta también es más fácil de usar, el primer ejemplo ya no se compila en la punta:

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

Creo que este problema también se mitiga aún más con # 43217, ya que debe importar "incrustar" para declarar variables de tipo embed.Bytes y embed.String todos modos. Y el compilador (pero no go / types o cmd / vet) también reporta un error si aplica la directiva //go:embed a una variable de un tipo incorrecto.

@mdempsky , sin embargo, la compilación accidental en una versión anterior de Go no fallará y podría dar una falsa sensación de que la inserción funcionó.

Las versiones anteriores de Go no tienen el paquete integrado, por lo que import "embed" fallará. Es cierto que existe el riesgo de que los usuarios puedan escribir:

package p

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

y Go 1.15 y versiones posteriores la aceptarán en silencio. Pero Go 1.16 y versiones posteriores no lo aceptarán. Al menos no hay programas que se compilen tanto con Go 1.15 como con Go 1.16 y tengan una semántica diferente (debido al paquete incrustado, al menos).

Creo que una solución apropiada (parcial) aquí sería que cmd / compile dejara de aceptar directivas //go: desconocidas. Por ejemplo, otra limitación de la propuesta actual relativa a la sintaxis incorporada de Go es que si escribe mal la directiva //go:embed (digamos //go:enbed , //go;embed , o // go:embed con un espacio), también se ignorará en silencio. (Mientras que los errores tipográficos como enbed.Bytes("foo.txt") causarían un error de verificación de tipos, incluso con go / types sin modificar).

Un gran punto sobre las directivas go: no validadas. Si eso se hiciera cumplir, ayudaría a aliviar los errores tipográficos difíciles de detectar.

Otro pensamiento que tenía ahora es que mis herramientas están configuradas para agregar / eliminar importaciones automáticamente según sea necesario. Si mis herramientas están desactualizadas, ¿tengo que luchar para que no elimine la importación embed "no utilizada"? Me di cuenta de que está resuelto si uso embed.String , etc., pero se supone que el uso normal de []byte] y string es completamente válido. Esto podría ser frustrante para un nuevo gopher que está seleccionando fragmentos web para que algo funcione si no ve los alias específicos de embed.* .

pero se supone que el uso regular de []byte] y string es completamente válido.

No lo serán si se acepta el número 43217. Recomiendo leer sobre # 43216 y # 43217. Ambos han recibido un apoyo abrumadoramente positivo hasta ahora y parece muy probable que los acepte. (Sin embargo, no estoy en el comité de revisión de propuestas).

Gracias, cuando leí # 43217 por primera vez, me perdí la palabra clave
"have" para usar los tipos embed.* .

Creo que la única preocupación que me queda es la última que señaló.

El jueves, 17 de diciembre de 2020, 20:24 Matthew Dempsky [email protected]
escribió:

pero se supone que el uso de [] bytes] y cadenas regulares es completamente válido.

No lo serán si # 43217 https://github.com/golang/go/issues/43217 es
aceptado. Recomiendo leer sobre # 43216
https://github.com/golang/go/issues/43216 y # 43217
https://github.com/golang/go/issues/43217 . Ambos han recibido
apoyo abrumadoramente positivo hasta ahora, y parece muy probable que sea aceptado
a mi. (Sin embargo, no estoy en el comité de revisión de propuestas).

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/golang/go/issues/41191#issuecomment-747808153 , o
darse de baja
https://github.com/notifications/unsubscribe-auth/ADLZABJWBJX475BVYDVD6ODSVKVOTANCNFSM4QTHVTUA
.

¿Fue útil esta página
0 / 5 - 0 calificaciones