Go: embed, cmd / go: adiciona suporte para arquivos incorporados

Criado em 2 set. 2020  ·  114Comentários  ·  Fonte: golang/go

Em julho, @bradfitz e eu postamos um rascunho de design para arquivos incorporados . O documento tem links para um vídeo, código de protótipo e uma discussão do Reddit.

O feedback sobre esse design foi extremamente positivo.

Proponho adotar o projeto de projeto de arquivos embutidos para Go 1.16, com um acréscimo, sugerido na discussão, para simplificar o caso de acesso direto aos bytes em um único arquivo embutido.

Contanto que um arquivo importe "embed" ( import _ "embed" se necessário), será permitido usar //go:embed nomeando um único arquivo (sem padrões glob ou correspondência de diretório permitidos) para inicializar uma variável simples string ou []byte :

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

A importação é necessária para sinalizar o arquivo como contendo //go:embed linhas e precisando de processamento. Goimports (e gopls etc) podem aprender esta regra e adicionar automaticamente a importação em qualquer arquivo com //go:embed conforme necessário.

O design de arquivos incorporados depende do design de rascunho da interface do sistema de arquivos, que também propus adotar em # 41190.

Este problema é _somente_ sobre a adoção do design de arquivos incorporados , pressupondo que o design da interface do sistema de arquivos também seja adotado. Se esta proposta for aceita antes do design da interface do sistema de arquivos, nós simplesmente esperaremos pelo design da interface do sistema de arquivos antes de começar as mudanças.

Proposal Proposal-Accepted

Comentários muito úteis

Nenhuma mudança no consenso, então aceita.

Todos 114 comentários

Seria um erro ter uma diretiva //go:embed sem importar embed ?

@jimmyfrasche Sim, quinto ao último marcador da lista em https://go.googlesource.com/proposal/+/master/design/draft-embed.md#go_embed -directives.

@rsc Talvez eu tenha perdido no rascunho, mas não vejo a capacidade de incorporar um único arquivo que você mencionou em seu comentário.
Além disso, você seria capaz de incorporar um único arquivo como uma string const também?
Obrigado por esta ótima proposta.

@pierrec Não está no documento de rascunho (a "uma adição" é o texto no comentário acima). As cadeias de const podem acabar desempenhando um papel na decisão se um tipo de programa verifica, o que significaria que todos os verificadores de tipo precisariam entender // go: embed'ed consts. Em contraste, se nos limitarmos aos vars, os verificadores de tipo não são muito sábios e podem ser deixados sozinhos. Parece que provavelmente deveríamos nos limitar a vars.

Há algum motivo específico para você querer um const em vez de um var? Usá-los deve ser quase o mesmo no que diz respeito à eficiência. (Referências a strings const acabam compilando a quantidade de referências a vars ocultos de qualquer maneira.)

Obrigada pelo esclarecimento. Eu tendo a incorporar ativos estáticos como strings const no momento, é por isso que perguntei. Eu também estou bem com vars!

Interessante, então eu poderia fazer algo como:

//go:embed version.txt
var Version string

E potencialmente até mesmo ter um comentário //go:generate para gerar version.txt. Isso eliminaria um grande caso de uso para makefiles / ldflags.

É um erro se o arquivo não for encontrado? Em caso afirmativo, onde tecnicamente se considera que o erro ocorre? Tempo do link?

Podemos ter certeza de que go: embed executa depois de go: generate para que possamos fazer coisas como gerar versões facilmente, etc?

Podemos ter certeza de que go: embed executa depois de go: generate para que possamos fazer coisas como gerar versões facilmente, etc?

Do meu entendimento, go:generate ocorrerá com go generate enquanto go:embed acontecerá com go build .

@carlmjohnson Sim, é sempre um erro dizer //go:embed foo onde foo não existe.
O erro ocorre ao compilar o arquivo de origem que contém essa linha.
(Se você remover foo depois de compilar aquele arquivo fonte, você ainda não conseguiria uma etapa de link - o comando go notaria que o pacote precisa ser reconstruído porque foo foi excluído.)

Acho que esta proposta não está completa sem dizer algo sobre ETag.
https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fzi0pok/

@ tv42 , sim, faremos a ETag funcionar. Não tenho certeza de qual é a forma disso, mas vamos.
(Também afirmado em https://github.com/golang/go/issues/35950#issuecomment-685845173.)

Duas Três coisas que notei ao trabalhar com mjibson/esc :

  • Como go:embed não precisa gerar arquivos go para incorporar como sistema de arquivos somente leitura, isso tiraria a dor de alterar os carimbos de data / hora nos arquivos go:generate ed que desafiavam git porcelain testes em CI- muito bom
  • Uma coisa que não encontrei na proposta, mas que precisaria, é a capacidade de recarregar os arquivos incorporados durante os ciclos de desenvolvimento. Usando mjibson/esc eu posso fazer isso instruindo-o a usar o sistema de arquivos local (embora ele não pegue novos arquivos) e mude o comportamento usando tags de construção. Estou me perguntando o que isso pode caber na proposta.
  • Atualizar Outra coisa que me lembro é que esc necessário para ser capaz de remover (partes do) caminho de base de forma transparente para, por exemplo, exportar uma pasta de ativos como raiz da web.

Consideração posterior : acho que o segundo ponto poderia ser remediado em conjunto com a proposta io/fs , em que eu usaria o sistema de arquivos incorporado ou ao vivo para inclusão? Implementar a remoção de caminho como io/fs middleware?

@andig Você já pode remover prefixos ao servir um sistema de arquivos por HTTP . Eu concordo que o recarregamento ao vivo pode ser feito por uma biblioteca de terceiros envolvendo um io/fs .

Mais uma coisa: se bem entendi, embed irá considerar os arquivos localmente para o pacote e proíbe .. . Meu projeto atual tem /assets e /server/ onde o último contém o código do servidor e hoje hospeda os arquivos gerados. Com esta proposta, o embed precisaria ser movido para a pasta raiz, pois os ativos não seriam acessíveis a partir do servidor. Isso impõe restrições de acessibilidade diferentes das importações normais. Eu queria saber se isso é necessário por motivos de segurança ou se as incorporações locais do módulo devem ser geralmente permitidas.

Mais uma coisa: se bem entendi, embed irá considerar os arquivos localmente para o pacote e proíbe .. . Meu projeto atual tem /assets e /server/ onde o último contém o código do servidor e hoje hospeda os arquivos gerados. Com esta proposta, o embed precisaria ser movido para a pasta raiz, pois os ativos não seriam acessíveis a partir do servidor. Isso impõe restrições de acessibilidade diferentes das importações normais. Eu queria saber se isso é necessário por motivos de segurança ou se as incorporações locais do módulo devem ser geralmente permitidas.

Você pode criar um arquivo emed.go em seu diretório de ativos e disponibilizar os ativos como seu próprio pacote para o resto do seu programa.

Outro objetivo explícito é evitar uma mudança de idioma. Para nós, a incorporação de ativos estáticos parece ser um problema de ferramentas, não um problema de linguagem.

Concordou. Na minha opinião, adicionar açúcar sintático na linguagem para dar suporte a essa mudança de ferramenta é uma mudança de linguagem. Tenho certeza de que isso é óbvio para os outros, mas é efetivamente um comentário como código.

Eu sinto fortemente que magic / sugar diminui a simplicidade e legibilidade da linguagem; é muito fácil perder um comentário mágico que incorpora um arquivo. Embora uma resposta a isso possa facilmente ser "ok, então não use", essa mudança significa que um revisor ainda precisa estar atento para outros que usam esse recurso e deve se lembrar que os comentários sobre as declarações de variáveis ​​podem quebrar compilações ou falhar em tempo de compilação.

Eu acredito que isso vai aumentar a confusão, diminuir a usabilidade da linguagem e irá resultar em binários opacos e grandes sem benefícios claros (em relação à última preocupação, isso levará até a um antipadrão de reconstrução de binários devido a alterações de arquivo simples ) Se go mod fosse permitido para --withNonGoCodeAssets , acredito que isso resolveria as necessidades da maioria dos desenvolvedores que não querem escrever pipelines de compilação mais complexos (presumo que a distribuição do usuário final é um subconjunto menor de o problema para os usuários).

@tristanfisher , entendo sua

Quanto ao inchaço, acho que veremos, mas não prevejo que os programas ficarão muito maiores do que já são. As pessoas já executam ferramentas que transformam arquivos arbitrários em código Go, fazem o check-in em seus repositórios e fazem com que o compilador os construa. O design remove alguma sobrecarga desse processo, mas não permite nada de novo. Talvez as pessoas abusem agora que é mais fácil de fazer, mas, no geral, não espero que isso seja um grande problema. (E se alguma dependência incorpora algo tão grande que incha seus binários, você sempre pode optar por não usar essa dependência.)

Quanto às reconstruções devido a alterações de arquivo simples, os únicos arquivos que podem acionar as reconstruções são aqueles em seu próprio módulo de nível superior, uma vez que as dependências são imutáveis. Se você descobriu que as reconstruções acontecem com mais frequência do que gostaria, a única explicação é (1) você está incorporando arquivos e (2) está modificando esses arquivos. Você teria o controle total de fazer algo sobre qualquer uma das causas. (Seria completamente diferente se a escolha de uma dependência sobre o que usar estivesse de alguma forma forçando reconstruções extras ou outras despesas para você. Mas esse não é o caso aqui.)

@rsc Concordo que podemos discordar e agradeço sua resposta. Minha impressão é que se for incluído por padrão no conjunto de ferramentas padrão e comentários podem levar a uma inicialização implícita de uma variável, então é uma mudança de idioma. Fora desse debate, acho que meu sentimento desagradável é em torno de mais diretivas como comentários "mágicos" que precisam ser memorizados por leitores de código (humanos). Isso pode levar à conclusão absurda de adicionar novos recursos por meio de comentários em bloco que são tratados no momento da construção.

Dito isso, se isso for adicionado ao ecossistema, ficarei grato pela necessidade de importar embed - isso é melhor do que nada como um "ei, atenção" ao auditar o código. Eu acho que go mod permitindo não .go resolveria a maioria dos casos de uso (eu imagino que a maioria das pessoas irá globar arquivos para servidores da web) e também viveria inteiramente em ferramentas.

Acho que seu ponto sobre o vinculador é bom. Isso também ajuda a explicar meus sentimentos sobre isso: se o próprio usuário final (por exemplo, não alguém que simplesmente importa um pacote) está tomando a decisão, não há como ser surpreendido por bolhas de não-código chegando. Minhas preocupações nascem da revisão / formação de pares no trabalho de outras pessoas e nas responsabilidades "tecnológicas", e é por isso que senti a necessidade de responder.

Acho que "vamos ter que ver" resume bem (sou mais cínico sobre inchaço / uso indevido).

Vou ler o rascunho do design esta noite, até agora parece bom da perspectiva do TinyGo.

Eu só queria esclarecer uma coisa:

Por outro lado, projetos como TinyGo e sistemas de destino U-root com mais RAM do que disco ou flash. Para esses projetos, compactar ativos e usar a descompressão incremental em tempo de execução pode fornecer economias significativas.

Não sei sobre o U-root, mas para TinyGo os principais alvos são microcontroladores que normalmente têm muito mais flash do que RAM (geralmente um fator de 8 ou 16). Uma rápida olhada no projeto de rascunho parece sugerir que a ideia é manter os arquivos em memória somente leitura, o que funcionaria bem para esses alvos: os arquivos incorporados podem ser lidos diretamente do flash. Provavelmente, não seria desejável que os destinos do TinyGo descompactassem arquivos em tempo de execução.

A proposta io / fs da qual isso depende parece estar bloqueada em problemas Readdir / FileInfo, em discussão em # 41188 e anteriormente # 40352.

Elaborei uma API para substituí-los em https://github.com/golang/go/issues/41188#issuecomment -686283661

@andig

Uma coisa que não encontrei na proposta, mas que precisaria, é a capacidade de recarregar os arquivos incorporados durante os ciclos de desenvolvimento.

embed.Files implementa fs.FS, portanto, tudo que você precisa fazer é usar dev vs! dev build tag para alternar uma variável entre embed.Files e o FS real.

Eu preenchi o número 41265. Ele oferece uma nova API ReadDir () para io / fs.

Tenho preocupações semelhantes às de @tristanfisher . Go tem usado comentários mágicos como diretivas de compilador há muito tempo (desde o início?), Mas eles são feitos para casos extremos e é raro que apareçam no código. Dada a popularidade da incorporação de conteúdo estático em binários Go, //go:embed provavelmente será mais comum. Talvez seja hora de considerar uma sintaxe diferente para as diretivas do compilador?

Apenas um lembrete de que alterar a sintaxe Go tem um custo muito alto. Praticamente todas as ferramentas Go que existem precisariam ser atualizadas e / ou consertadas para suportar a nova sintaxe, por exemplo.

Não os considero comentários mágicos. As linhas que começam com //go: são diretivas e podem ser definidas como tal nas especificações. Não há muita diferença semântica entre //go:embed , @embed , [[embed]] ou qualquer outro número de variações de sintaxe, exceto que o prefixo //go: já é tratado como não -code por ferramentas Go. (meu editor destaca essas linhas de maneira diferente, por exemplo)

@mvdan Se esta proposta acontecer, a sintaxe Go mudou. Apenas mudou de uma forma que não quebra o ferramental existente. Talvez isso pareça pedante.

@iand não sou muito exigente quanto à sintaxe específica das diretivas do compilador. Só acho que precisa ser formalizado em algum momento e as regras especificadas.

Acho que essa proposta é uma boa ideia. Isso resolve um problema comum. Minha preocupação é que o custo de sua adoção seja um pouco mais explícito.

@jonbodner Compartilho suas preocupações sobre comentários mágicos. Mas, até certo ponto, as regras são especificadas por # 37974.

@networkimprov , esta não é a proposta io / fs. Por favor, pare de comentar sobre ReadDir aqui.

@jonbodner

Não sou muito exigente quanto à sintaxe específica das diretivas do compilador. Só acho que precisa ser formalizado em algum momento e as regras especificadas.

Gostaria apenas de salientar que tomamos a decisão de usar //go: para marcar as diretivas do conjunto de ferramentas Go quando
adicionamos (uso limitado) a anotação
Adicionamos //go:noescape para autores de montagens em 2013.
Adicionamos //go:generate em 2014.
Provavelmente estamos
Há outros; esses são apenas os destaques.
Você pode pensar em //go: significando #pragma de C se isso ajudar.

Neste ponto, a convenção está muito bem estabelecida.
Escolhemos essa sintaxe em 2012 porque
(1) obviamente não é um comentário para uma pessoa;
(2) ferramentas que não sabem sobre os comentários irão ignorá-los porque são comentários; e
(3) generaliza para outras ferramentas (s / go / yourtool /).

E, como Ian disse, # 37974 formalizou a sintaxe de comentário generalizada exata, para valer a pena.

Com base na discussão acima, parece uma aceitação provável .
(Novamente, supondo, mas separado da proposta FS.)

Nenhuma mudança no consenso, então aceita.

Estou ansioso para colocar minhas mãos no embed - isso já pode ser testado no master ou há planos de enviá-lo como experimento durante o ciclo de 1,15?

@andig , Go 1.15 já foi lançado. Ainda espero que esteja no Go 1.16 e no ramo de desenvolvimento este mês.

@rsc 1.16 disponível?

@septs , não, ainda estamos trabalhando no Go 1.16. O congelamento do código ocorre em 31 de outubro, com lançamento previsto para 1º de fevereiro.

versão 2021Q1 ou 2021Q2 mais rápida?

@septs , pare de fazer perguntas sobre os lançamentos Go neste tópico. Mais de vinte pessoas seguem e são notificadas. Consulte https://golang.org/wiki/Questions e https://github.com/golang/go/wiki/Go-Release-Cycle.

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

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

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

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

Mudança https://golang.org/cl/243945 menciona este problema: cmd/go: add //go:embed support

Um detalhe que surgiu na revisão da implementação é que "Arquivos" como um substantivo singular é muito estranho ("Um arquivo contém ...").

A escolha de embed.Files para o nome é anterior à proposta io / fs e também ao suporte para string e [] byte.
Dados esses dois desenvolvimentos, uma maneira aparentemente sensata de resolver o problema de "A guarda de arquivos" é chamá-lo de FS em vez de Arquivos.

Então, as três maneiras de incorporar e imprimir dados são:

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

Isso parece mais claro sobre o que você obtém: uma string, um [] byte ou um FS.
Ou seja, a maior parte da funcionalidade do embed.F * vem de ser um fs.FS, e chamá-lo de FS torna isso mais claro do que chamá-lo de Arquivos.

Fiz essa mudança no meu último rascunho do pacote de implementação de CL incorporado, mas gostaria de voltar aqui e ver se há alguma objeção à mudança de nome.

(Uma mudança mais radical seria var f fs.FS vez de var f embed.FS , mas isso impediria qualquer método em f diferente de Open. Por exemplo, acima, tendo ReadFile é conveniente e não seria possível. E, em geral, aprendemos que usar um tipo concreto para algo que pode querer adicionar métodos posteriormente é uma boa proteção para o futuro em comparação com o uso direto de um tipo de interface.)

Acho que renomear é uma boa mudança.

Em relação à mudança mais radical:

  • Se usássemos fs.FS , ainda precisaríamos do pacote embed ? Acho que o valor dinâmico ainda deve ter algum tipo, que mora em algum pacote? Acho a ideia de não ter que adicionar um pacote a mais.
  • Não acho "não podemos adicionar métodos" muito convincente, porque IMO f.ReadFile(…) não é significativamente menos conveniente do que fs.ReadFile(f, …) .
  • Eu concordo que os tipos de concreto são melhores em geral, então isso é uma vantagem para mantê-los embed.FS
  • Outra pergunta: embed.FS usa receptores de ponteiro ou receptores de valor? IMO ter que passar em torno de &f é estranho, usar receptores de valor é um pouco inesperado. No entanto, também podemos permitir var f *embed.FS . Se a variável tiver um tipo de interface, essa questão vai embora.

No geral, eu ainda concordo que usar o concreto embed.FS é melhor - se nada mais, então para fins de documentação.

Agora que você mencionou, não acho que entendi bem: podemos incorporar diretórios, certo?

Sim como um embed.FS que implementa fs.FS.

@Merovius , embed.FS usa receptores de valor. embed.FS é uma estrutura de uma palavra contendo um único ponteiro, então não há sobrecarga real para fazer isso, mas significa que você pode atribuí-los e usá-los sem se preocupar com * se \ & s em todos os lugares.

@ chabad360 , sim, você pode incorporar diretórios.

E quanto aos links simbólicos?

@ burik666 , consulte https://golang.org/s/draft-embed-design para obter detalhes, mas não, você não pode incorporar um link simbólico.

será possível incorporar e usar bibliotecas C dinâmicas? em caso afirmativo, como usaríamos o caminho de incorporação em #cgo cabeçalhos, como: #cgo LDFLAGS: -L./lib -lmylib -Wl,-rpath=./lib ?

@benitogf Presumo que a única maneira real de fazer isso seria gravá-los no disco e usar dlopen . Não consigo imaginar como você poderia dizer ao carregador dinâmico como localizar arquivos incorporados. Além disso, se você deseja agrupar em código C, a ligação estática pareceria mais apropriada de qualquer maneira, não?

@benitogf Embedding permite que você coloque um arquivo do disco em um byte [] em seu programa convenientemente, nada mais.
Se você tiver uma maneira de usar uma biblioteca C dinâmica que já está em seu programa na forma de um byte [], a incorporação o ajudará a obter um arquivo de disco lá. Caso contrário, não.

a ligação estática pareceria mais apropriada de qualquer maneira, não?

@Merovius concordou, mas tenho vários casos de uso trabalhando com fornecedores que fornecem apenas bibliotecas dinâmicas

Se você tem uma maneira de usar uma biblioteca C dinâmica que já está em seu programa na forma de um byte []
a única maneira real de fazer isso seria gravá-los no disco e usar dlopen

escrever a biblioteca embutida do [] byte para o sistema de arquivos e usar dlopen parece ok, embora ter os arquivos embutidos opcionalmente "despejados" no sistema de arquivos na construção / execução para que o cabeçalho #cgo possa acessá-los seria útil, não apenas para cgo imho

Tentando fazer isso agora; uma verruga com a diretiva go:embed é que se eu incorporar build/* , os nomes de arquivo ainda terão o prefixo build/ . Se eu quiser servir esse diretório por meio de http.FS , não há uma maneira fácil de _adicionar_ o prefixo que é necessário para acessá-los, se necessário (sem escrever um invólucro, que então atinge o problema de precisar listar todos os métodos potenciais que o FS pode ter ...).

por exemplo:

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

Eu sei que a intenção é escrever go:embed foo/* bar/* baz.ext e obter todos esses arquivos, mas acho que será muito comum simplesmente incorporar um diretório e servi-lo como ativos estáticos por meio do pacote http. Espero que isso seja um problema quando as pessoas mudam de coisas como http.Dir("static") ou pkger.Dir("/internal/web/static") onde o prefixo já foi manipulado, para o novo embed.FS .

Não tenho certeza de como arquivar isso, pois é uma espécie de interação com embed , io/fs e net/http .

@zikaeroh Escrever um http.Handler também funcionaria, certo? Esse é apenas um método e ainda existe http.HandlerFunc . Talvez a biblioteca padrão possa até mesmo fornecer uma para espelhar http.StripPrefix (algo como http.AddPrefix ou http.ReplacePrefix ).

Potencialmente, embora pareça um pouco estranho modificar a solicitação HTTP para contornar a implementação do FS (ao contrário de um generalizado "dê-me um FS que é um subdiretório de outro FS", que não é simples com métodos opcionais). Não seria a coisa mais eficiente, retirar e depois adicionar outro prefixo novamente (dados http.Request cópias), mas vou tentar mais tarde. É pelo menos _diferente_ do que o esquema atual de coisas em que você tem que trabalhar com o pedido, eu suponho.

Eu tenho alguns outros lugares em que uso dados estáticos, não por meio do pacote http, para os quais terei que encontrar uma correção semelhante.

Se eu tiver que dar uma olhada em como isso está sendo implementado, onde posso ver. Uma filial onde está sendo implementado?

Foi sugerido antes incorporar os arquivos no local, ou seja, fazer isso no diretório de construção e depois importá-lo. Isso eliminaria o prefixo de construção. Em seguida, use o manipulador para adicionar o prefixo necessário. Não tenho certeza de como excluir o arquivo go que está fazendo a incorporação da própria incorporação. Consulte https://github.com/golang/go/issues/41191#issuecomment -686621090

Foi sugerido antes incorporar os arquivos no local, ou seja, fazer isso no diretório de construção e depois importá-lo. Isso eliminaria o prefixo de construção. Em seguida, use o manipulador para adicionar o prefixo necessário. Não tenho certeza de como excluir o arquivo go que está fazendo a incorporação da própria incorporação. Veja # 41191 (comentário)

Infelizmente, isso não é bom para diretórios produzidos por outras ferramentas, por exemplo, a saída de, digamos, uma construção de webpack ou CRA (onde geralmente são limpos de antemão, e não registrados). Prefiro hackear os nomes dos arquivos.

Foi sugerido antes incorporar os arquivos no local, ou seja, fazer isso no diretório de construção e depois importá-lo. Isso eliminaria o prefixo de construção. Em seguida, use o manipulador para adicionar o prefixo necessário. Não tenho certeza de como excluir o arquivo go que está fazendo a incorporação da própria incorporação. Veja # 41191 (comentário)

Infelizmente, isso não é bom para diretórios produzidos por outras ferramentas, por exemplo, a saída de, digamos, uma construção de webpack ou CRA (onde geralmente são limpos de antemão, e não registrados). Prefiro hackear os nomes dos arquivos.

Se você usa um sistema tão grande de plug-ins como o webpack, é fácil apenas instalar outro plug-in do webpack para gerar o embed.go junto com os próprios ativos. Se você estiver usando algo mais simples com um makefile ou script de shell, também é fácil gerar o arquivo .go a partir daí.

@zikaeroh

em oposição a um generalizado "dê-me um FS que é um subdiretório de outro FS", que não é simples com métodos opcionais

Deve ser simples. Lidar com o problema da embalagem fazia parte do processo de design. Em particular, o wrapper deve implementar todos os métodos opcionais, apenas chamando a função auxiliar apropriada em fs . Se isso não funcionar, é preocupante e seria ótimo obter alguns detalhes.

Além disso, IMO, uma implementação de tal invólucro (que retira um prefixo) deve ser fornecida por io/fs (análogo a io.LimitWriter , etc). Eu diria que o único motivo pelo qual ainda não aconteceu é o tempo.

@andig O problema em fazer isso é que o arquivo Go contendo a diretiva embed e a variável também fica visível no FS (e seria servido por HTTP ou poderia ser exposto de outra maneira).

O problema em fazer isso é que o arquivo Go contendo a diretiva embed e a variável também fica visível no FS (e seria servido por HTTP ou poderia ser exposto de outra maneira).

Uma maneira de remediar isso seria adicionar a capacidade de excluir arquivos / pastas específicos da incorporação ( @rsc ?)

Uma maneira de remediar isso seria adicionar a capacidade de excluir arquivos / pastas específicos da incorporação ( @rsc ?)

A proposta foi aceita há mais de um mês e já está implementada; Não acho que grandes mudanças de design, como ser capaz de excluir caminhos, sejam razoáveis ​​neste momento. Se você tiver um problema com o design implementado que não consiga contornar, sugiro preencher um relatório de bug separado com detalhes, que pode então ser rastreado antes do lançamento final 1.16.

@Merovius

Deve ser simples. Lidar com o problema da embalagem fazia parte do processo de design. Em particular, o wrapper deve implementar todos os métodos opcionais, apenas chamando a função auxiliar apropriada em fs. Se isso não funcionar, é preocupante e seria ótimo obter alguns detalhes.

Como funcionaria a remoção do prefixo para Glob ?

@icholy presumo 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
}

Talvez com alguns cuidados adicionais.

Brincando com isso no gotip, noto que incluirá arquivos .DS_Store. Isso é praticamente inevitável, eu acho, mas me preocupo que a inclusão de arquivos de ponto levará à inclusão acidental de arquivos. Talvez os documentos devam ter um forte aviso sobre isso?

Meu shell não inclui arquivos de ponto em * , então se eu quiser incluí-los, tenho que usar * .* . Esta pode ser uma forma (talvez igualmente surpreendente) de fornecer um nível de controle.

Não tenho certeza do que pensar - IMO, arquivos de ponto realmente não deveriam ser tratados de forma diferente pelo padrão, mas OTOH, o exemplo .DS_Store parece algo que realmente deveria ser tratado.

Por que não fazer apenas git clean -dffx && go build ? Se os arquivos DS_Store estiverem em git, eles serão incluídos na construção. Se não forem, não serão. Isso também funcionará bem com o gitignore.

Você deveria estar construindo com um checkout VCS limpo, de qualquer maneira. Se você adicionar arquivos temporários aleatórios, eles podem chegar à compilação final e você não pode saber com quais arquivos as pessoas ficarão. Podemos querer documentar isso.

@mvdan Eu concordo em princípio, mas na prática,


Re: incorporando http.FileServers

Se você usa um sistema tão grande de plug-ins como o webpack, é fácil apenas instalar outro plug-in do webpack para gerar o embed.go junto com os próprios ativos.

Isso é verdade, mas é muito complicado. O objetivo do embed.FS é reduzir a necessidade de soluções alternativas malucas de Makefile. Acho que a solução simples é ter fs.WithPrefix(string) fs.FS que bloqueia um FS em um subdiretório. Achei que houvesse alguma discussão sobre isso na proposta, mas não consigo encontrar agora. Talvez tenha sido apenas debatido no Reddit ou algo assim.

Uma maneira de remediar isso seria adicionar a capacidade de excluir arquivos / pastas específicos da incorporação ( @rsc ?)

Pode ser algo como

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

A diretiva embed-exclude poderia apenas fazer um filtro global nos arquivos aceitos e remover quaisquer correspondências ...

Se quisermos evitar adicionar mais a esta proposta, também pode ser uma regra lint que verifica sistemas de arquivos embutidos para dotfiles potencialmente inesperados e avisa para que você possa corrigir sua construção para removê-los.

Ou exclua arquivos de ponto por padrão, a menos que sejam especificamente mencionados adicionando. * Ou semelhante.

Isso ainda não resolveria expor um arquivo assets.go. Quanto à proposta já implementada, vale ressaltar que a questão sobre a geração do ativo foi levantada durante a fase de discussão. Provavelmente não é perigoso ter um (caso contrário vazio, exceto para a diretiva embed) assets.go exposto, mas seria mais limpo não tê-lo. Como de costume, existem todos os tipos de soluções alternativas que podem ser aplicadas.

Provavelmente não é perigoso ter um (caso contrário vazio, exceto para a diretiva embed) assets.go exposto, mas seria mais limpo não tê-lo.

Eu concordo que é muito improvável que isso seja um problema, mas eu odiaria ver qualquer código-fonte fechado vazado acidentalmente devido a uma configuração incorreta, se pudermos facilitar a configuração correta das coisas.

Não quero ver um vazamento secreto de alguém que não percebeu que seu arquivo .env foi incorporado por engano.

Se alguém usar //go:embed static/* e houver static/.env ou static/.super-secret , você não diria que o usuário realmente quis incluir esses arquivos? Caso contrário, por que eles estariam no diretório estático?

Eu entendo que isso depende do que o usuário espera e o que * significa na maioria dos contextos, mas eu pessoalmente acho que https://golang.org/pkg/path/filepath/#Glob semântica é nosso único bom opção. É o mais simples e com o qual a maioria dos usuários de Go estará acostumada no contexto de desenvolvimento de Go.

Acho que alertar contra os perigos de incorporar * é uma boa ideia em qualquer caso, porque em uma quantidade significativa de casos, pode-se reduzir a chance de erro usando globs mais específicos como *.png .

Além disso, eu ainda encorajo você a registrar um problema separado que pode ser rastreado em relação à versão 1.16, escrito do ponto de vista de um relatório de bug. Essa proposta foi aceita e implementada, então imagino que será encerrada muito em breve. Por exemplo, você poderia formular o relatório de bug da seguinte forma: o suporte a arquivos incorporados facilmente leva à inclusão de arquivos indesejados (e dá alguns exemplos).

Se alguém usar // go: embed static / * e houver um static / .env ou static / .super-secret, você não diria que o usuário realmente pretendia incluir esses arquivos? Caso contrário, por que eles estariam no diretório estático?

Há uma grande quantidade de casos esquivos, por exemplo, você abriu uma sessão de edição com o vim, mas não a fechou, e ele criou o arquivo .*.swp com alguns conteúdos que você gostaria que ninguém visse.

Movendo a discussão para # 42321.

Isso seria muito apreciado pela equipe Prisma para nosso cliente Database Go , já que usamos um mecanismo de consulta em tempo de execução que é escrito em ferrugem e precisa estar no binário construído go de alguma forma.

A maneira como estamos fazendo isso atualmente é empacotar o binário em um arquivo .go em tempo de go: generate, mas o tamanho do arquivo é muito maior do que quando é um arquivo .gz binário.

Incorporações nativas tornariam isso muito melhor, de modo que pudéssemos incorporar diretamente um arquivo .gz no binário go final.

Se alguém usar //go:embed static/* e houver static/.env ou static/.super-secret , você não diria que o usuário realmente quis incluir esses arquivos?

Não, eu não faria.

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

Veja meu comentário posterior em https://github.com/golang/go/issues/42328#issuecomment -720169922.

Eu adoro a ideia de fazer arquivos / modelos estáticos incorporáveis, o que definitivamente pode aumentar muito a produtividade do desenvolvedor.

Mas devemos inovar outra tag (por exemplo, @ ou qualquer outra) além de reutilizar este // , que deveriam ser comentários?

A partir de agora, o // foi usado em excesso, eu acho, pense no seguinte:

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

Mas devemos inovar outra tag (por exemplo, @ ou qualquer outra) além de reutilizar este // , que deveriam ser comentários?

Essa é uma discussão separada e, embora seja bom que as diretivas não sejam comentários, muito do código go1 compat já depende disso, então é provável que não vá mudar.

Mas devemos inovar outra tag (por exemplo, @ ou qualquer outra) além de reutilizar este // , que deveriam ser comentários?

Tentei encontrá-lo e não consegui, mas me lembro de haver uma decisão de padronizar em //go:<word> para esses tipos de diretivas.
Não parece uma boa ideia mudar a sintaxe ao redor, dada a decisão de convergir expressamente para eles.
(Além disso, é claro, eles são comentários especificamente para que o compilador os ignore - essas diretivas são específicas para a ferramenta go, portanto, não devem vazar para a linguagem adequada)

Eu vi uma menção sobre o problema de io/fs que embed.FS suporta ETag: https://github.com/golang/go/issues/41190#issuecomment -737702433

Tentei executar um teste para ele, mas não estou vendo um conjunto de cabeçalhos de resposta ETag . Talvez eu esteja entendendo mal o uso. Devo esperar ver um aqui? https://play.golang.org/p/Wq5xU5blLUe

Eu acho que não. http.ServeContent (usado por http.FileServer ) inspeciona o cabeçalho ETag, mas não o define, AIUI.

Em um comentário acima, Russ disse que a ETag será feita para funcionar . A dificuldade é como fazer embed.FS comunicar a http.FileServer as informações necessárias para definir ETag ou outros cabeçalhos de cache. Provavelmente, deve haver um problema de rastreamento separado para isso.

Pessoalmente, eu diria que embed.FS deve usar o tempo do último commit do módulo relevante como ModTime . Isso corresponderia aproximadamente a debug.BuildInfo , portanto, não afetaria a reprodutibilidade. Embora eu não tenha certeza de como isso é definido para commits que não correspondem a um lançamento marcado e ainda deixaria a questão de como defini-lo para compilações de árvores de trabalho sujas.

Mas acredito que @rsc tenha uma boa solução em mente :)

Não tenho certeza se entendi o comentário sobre "tempo de confirmação"; se a fonte do módulo for um arquivo zip, não há "confirmação". Não vejo nenhum momento mencionado em debug.BuildInfo ou debug.Module .

Mais importante, eu diria que qualquer mecanismo baseado em carimbo de data / hora é estritamente inferior a uma etag adequada (baseada em hash de conteúdo).

@ tv42 Cada versão do módulo é a) uma versão semântica derivada de uma tag (que aponta para um commit) ou b) uma pseudo-versão contendo um hash de commit. Eu penso? Pelo menos no git. Posso estar entendendo mal alguma coisa.

Mais importante, eu diria que qualquer mecanismo baseado em carimbo de data / hora é estritamente inferior a uma etag adequada (baseada em hash de conteúdo).

Eu não tenho tanta certeza. Ele precisa de algum canal lateral para comunicar o hash ou o servidor precisa calcular o hash do arquivo quando solicitado (o que parece muito caro). Afinal, net/http não sabe, a priori, se o conteúdo de fs.FS pode mudar. O resultado final de uma ETag baseada em hash pode justificar o custo de adicionar tal canal lateral (como uma interface opcional), mas não parece obviamente estritamente melhor.

Além disso, eu diria que suportar pelo menos também uma abordagem baseada no tempo significa que você pode trabalhar com mais clientes. Não tenho nenhum dado para apoiar isso, porém (ou seja, não sei se e quantos clientes existem que podem suportar If-Modified-Since mas não ETag e vice-versa).

Mas, realmente, não me importo muito com a abordagem escolhida. Queria apenas mencionar a opção de usar o momento em que uma versão do módulo foi marcada.

Os módulos

$ 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 haver um carimbo de data / hora no arquivo *.info o acompanha, mas não tenho ideia se ele é confiável ou está disponível para as ferramentas.

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

Mesmo assim, qual carimbo de data / hora deve ser: usar incorporar no módulo principal (aquele em que você executa go build )?

Pessoalmente, no contexto de arquivos estáticos que são imutavelmente incorporados no binário, o hash parece simplesmente superior aos carimbos de data / hora em todos os aspectos (exceto o suporte legado de antes de ETag existir, pré-HTTP / 1.1, anos 90), e não tem nenhuma fonte possível de confusão em sistemas distribuídos e compilações simultâneas. Duvido seriamente que existam clientes que não entendem mais a ETag, e eles certamente não farão a transição HTTP / 2.

Um canal lateral para comunicar o hash parece a coisa certa para mim; uma interface FS opcional para retornar um hash do arquivo. (E talvez outro para solicitar um algoritmo de hash específico, se acontecer de ele ter ?. Alguns casos de uso de serviço realmente precisam especificamente de sha256 / sha1 / md5, não apenas de "algum hash"). Um FS que não tem uma resposta barata o suficiente pode optar por não implementá-la.

(Embora os hashes modernos sejam gigabytes / segundo / núcleo mesmo quando criptograficamente seguros, dezenas de gigabytes / segundo para os menos seguros e fáceis de armazenar em cache com base na chamada de estatísticas. Basta suportar ETag em todos os lugares, pessoal.)

Estive pensando sobre esta proposta hoje. Estou feliz em ver essa funcionalidade incluída no conjunto de ferramentas Go e agradeço todo o pensamento e esforço investidos na proposta até o momento. Obrigado por propor isso e levar isso adiante.

Eu tenho duas perguntas e, em seguida, uma sugestão (reconhecendo que o período de discussão da proposta já passou e já está no congelamento da versão):

Tipos permitidos

Percebo que o código atual espera que as variáveis ​​sejam declaradas com a precisão ~ embed.FS ~, string ou []byte . Em particular, eles não são permitidos: []uint8 , ~ FS após a importação de pontos de "embed" ~, ou quaisquer outros tipos idênticos construídos usando apelidos de tipo. (Editar: esqueci como as importações de pontos funcionam no gc e li incorretamente o código para detectar embed.FS .)

Isso é intencional ou um bug?

Semântica de variáveis ​​do tipo []byte .

Não vejo qualquer menção de qual é a semântica de identidade esperada de []byte -variáveis ​​tipadas. Em particular, para variáveis ​​com escopo de função. Isso não é importante para variáveis ​​do tipo string - e embed.FS , porque elas fazem referência a dados imutáveis ​​que podem ser desduplicados com segurança. Mas é importante saber a semântica pretendida para variáveis ​​do tipo []byte .

Com a implementação atual, o programa de teste abaixo imprime false true (quando foo.txt não está vazio). Isso é pretendido / garantido?

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

Acho que a semântica mais parecida com Go para variáveis //go:embed seria a de literais compostos: que cada execução produz uma nova cópia.

Se não houver consenso sobre a semântica adequada para isso, podemos sempre trabalhar nisso e fazer com que o escopo da função seja []byte -typed incorpora um erro para Go 1.16: os usuários ainda podem declarar uma variável de nível de pacote se eles querem a semântica atual (uma fatia de byte por declaração de origem), ou use uma variável do tipo string e converta para []byte se eles quiserem uma semântica literal composta. Poderíamos então revisitar mais tarde qual comportamento os usuários se beneficiariam mais.

Evitando mais //go: diretivas

Não recomendo adicionar diretivas //go: para usuários finais que afetam a semântica do programa, e não acho convincentes os argumentos dados para favorecer //go:embed relação à sintaxe Go normal. Eu respeitosamente encorajo a reconsiderar essa decisão antes do lançamento do Go 1.16. (Mais uma vez, agradeço o quão atrasado é este pedido.)

Vou começar apontando que tenho um CL de prova de conceito funcionando em CL 276835 , que altera a API de incorporação de:

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

para

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

Da mesma forma, as funções embed.Bytes e embed.String estão disponíveis para incorporar um único arquivo e obtê-lo como um []byte ou string .

Respostas

Da mesma forma, uma variável embed.Files pode ser global ou local, dependendo do que for mais conveniente no contexto.

Ter embed.Files , etc. permite também usá-los em contextos de expressão, o que pode ser ainda mais conveniente.

É um erro usar // go: embed em um arquivo de origem que não importa "embed" (a única maneira de violar essa regra envolve truques de apelido de tipo).

Com embed.Files , etc., isso é um erro em virtude das funções que estão sendo exportadas pelo pacote embed.

Goimports (e gopls etc) podem aprender esta regra e adicionar automaticamente a importação em qualquer arquivo com // go: embed conforme necessário.

Nenhuma lógica especial de goimports ou gopls é necessária para estar ciente de que importar "embed" é a maneira adequada de corrigir o uso de embed.Files , etc.

Essa abordagem corrige o problema de verificação de tipo - não é uma mudança completa de linguagem - mas ainda tem uma complexidade de implementação significativa.

Notavelmente, CL 276835 é uma remoção líquida de código. Em particular, o código do compilador (que terá que ser reimplementado no gccgo e em outros compiladores) é muito mais simples.

Também espero que seja mais fácil ensinar go / types sobre a semântica diferenciada de embed.Files , etc. (ou seja, que eles só aceitam argumentos literais de string) do que ensiná-la sobre //go:embed .

O comando go precisaria analisar todo o arquivo de origem Go para entender quais arquivos precisam ser disponibilizados para incorporação. Hoje, ele analisa apenas até o bloco de importação, nunca expressões Go completas

Com CL 276835, o comportamento é o mesmo da dica: o comando go analisa todo o arquivo de origem Go em busca de arquivos que importam pacote incorporado, e apenas as importações de arquivos que não o fazem.

Reconhecidamente, para arquivos que importam embed, CL 276835 faz uma análise completa e analisa, enquanto tip faz uma varredura de string mais eficiente para //go:embed comentários. Eu acho que um algoritmo de uma passagem mais otimizado para encontrar embed.Files chamadas é possível, se desejado.

Também não estaria claro para os usuários quais restrições são colocadas nos argumentos para essas chamadas especiais: elas se parecem com chamadas Go comuns, mas só podem aceitar strings literais, não strings computadas pelo código Go e provavelmente nem mesmo constantes nomeadas (ou então o o comando go precisaria de um avaliador de expressão Go completo).

Isso não parece substancialmente diferente da diretiva //go:embed para mim: os usuários não têm nenhuma expectativa de quais argumentos podem usar lá até que os usem pela primeira vez. Além disso, de qualquer forma, os usuários receberão mensagens de erro do compilador se o usarem incorretamente, mas IDEs e outras ferramentas fornecerão automaticamente godocs e referências cruzadas melhores para embed.Files , etc.

@mdempsky FS após uma importação de ponto de embed é do mesmo tipo. Então, esse caso particular parece um "sim, tudo bem" direto (tentei e já funciona). Da mesma forma, byte é um apelido para uint8 , então []uint8 também é do mesmo tipo que []byte , embora um tanto surpreendentemente, isso não funcione agora . No entanto, eu diria que a semântica conforme implementada está bem, por enquanto - podemos sempre permitir mais tipos e / ou apelidos e / ou mesmo "o mesmo tipo subjacente" posteriormente, se houver necessidade.

Acho que a semântica mais parecida com Go para variáveis ​​// go: embed seria a de literais compostos: que cada execução produz uma nova cópia.

Tendo a concordar, essa era minha expectativa também. A princípio, pensei que isso poderia ser um artefato de análise de escape e o compilador perceber que você não altera os dados, mas não:

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

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

No entanto, "cada declaração var cria uma nova variável" também tem seus problemas, porque significa um 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))
}

iria alocar e copiar desnecessariamente. Talvez isso seja bom e seja superado pelos benefícios de uma semântica mais clara. Mas talvez seja também um sinal para proibir as incorporações locais de []byte conforme você mencionou.

Também espero que seja mais fácil ensinar go / types sobre as semânticas diferenciadas de embed.Files, etc. (ou seja, que eles só aceitam argumentos literais de string), do que ensiná-lo sobre // go: embed.

Apenas constantes de string, pelo menos, podem ser feitas no sistema de tipo Go.

Mas, go/types precisa saber sobre arquivos embutidos, com //go:embed ? Afinal, o tipo dessas variáveis ​​é bastante claro (com exceção das incorporações locais de []byte , conforme discutido).

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

Isso realmente é para ser mutável? Não consigo pensar em um único caso de uso em que gostaria de incorporar um ativo estático, mas alterá-lo em tempo de execução, então sinto que isso apenas habilitará bugs. Eu teria ficado mais feliz enfiando-o em .rodata e entrando em pânico se algo tentasse alterá-lo.

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

@Merovius escreveu:

@mdempsky FS após uma importação de ponto de embed é do mesmo tipo. Então, esse caso particular parece um "sim, tudo bem" direto (tentei e já funciona).

Obrigado. Esqueci como as importações de ponto funcionam dentro do compilador, por isso li mal o código. Eu deveria ter tentado antes de incluir aquele exemplo.

No entanto, eu diria que a semântica conforme implementada está bem, por enquanto - podemos sempre permitir mais tipos e / ou apelidos e / ou mesmo "o mesmo tipo subjacente" posteriormente, se houver necessidade.

[]byte e []uint8 são tipos idênticos sob a especificação Go, assim como inúmeros outros tipos construídos usando apelidos de tipo. Se não for aceitável que um compilador trate embed.Files("foo" + ".txt") diferente de embed.Files("foo.txt") , então o mesmo argumento deve se estender para não permitir que ele trate tipos com alias de maneira diferente.

Mas, go/types precisa saber sobre arquivos incorporados, com //go:embed ?

Não, não precisa, assim como não precisa saber sobre a semântica especial de embed.Files também. Mas para qualquer sintaxe, ainda pode haver valor em go / types, sendo capaz de alertar sobre o uso indevido.

-

@ tv42 escreveu:

Isso realmente é para ser mutável?

Evidentemente. O compilador Go organiza especificamente para string e embed.FS incorporações a serem desduplicadas e colocadas em rodata, enquanto []byte incorporações não são:

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

No entanto, é verdade que a proposta não parece ter abordado esse ponto.

Obrigado pelos comentários atenciosos. Eu apresentei dois problemas para acompanhamento:

# 43216 - remove o suporte para diretivas de incorporação em variáveis ​​locais
# 43217 - define os aliases de tipo String e Bytes que devem ser usados ​​com // go: embed

Eu não registrei um problema para a própria sintaxe // go: embed. Eu queria dizer o porquê aqui, para que não pareça a todos que estou apenas descartando isso.

Na época em que escrevi sobre o processo de proposta, passei muito tempo procurando maneiras de (e não) lidar com decisões em grandes grupos. Uma fonte que descobri que fazia muito sentido para mim foi a postagem “ Tomada de decisão aberta ” de John Ousterhout. Vale a pena ler a postagem inteira, mas vou apenas citar aqui a seção sobre Reconsideração:

A última regra para a tomada de decisão aberta é certificar-se de não reconsiderar uma decisão a menos que haja novas informações significativas. Esta regra tem duas partes. Primeiro, você deve estar preparado para corrigir decisões que se revelem erradas de maneira significativa. Isso é particularmente verdadeiro em startups: muitas decisões devem ser tomadas sem informações completas e, inevitavelmente, algumas delas acabarão por estar erradas. Uma decisão errada que é corrigida rapidamente causa pouco ou nenhum dano, mas uma decisão errada que não é corrigida pode ser catastrófica.

Por outro lado, você não deve reconsiderar uma decisão, a menos que novas informações significativas tenham surgido desde que a decisão original foi tomada. Se nenhuma informação nova estiver disponível, reconsiderar uma decisão provavelmente produzirá o mesmo resultado que a decisão original, desperdiçando o tempo de todos. Não é incomum que os funcionários me procurem semanas depois que uma decisão foi tomada: "John, votei contra a decisão de XYZ e, quanto mais penso nisso, mais convencido fico de que estava errado; realmente acho que precisamos reconsidere isso. " Minha resposta é "Quais são as novas informações que você tem?" Se a resposta for "nenhum", não reconsideramos. Em situações como essa ajuda ter um registro dos argumentos apresentados durante a discussão, para que você possa verificar se as novas informações são realmente novas. Se você torna muito fácil reabrir decisões, você acaba vacilando para frente e para trás onde nenhuma decisão é definitiva e os funcionários hesitam em implementar decisões porque não acreditam que sejam permanentes.

Se você quiser ter certeza de não reconsiderar muitas decisões, certifique-se de reunir amplamente as informações durante o processo de tomada de decisão. Se você não receber informações suficientes, aumentará a probabilidade de que novas informações significativas surjam após a decisão, o que significa que você terá que reconsiderar. Se você fizer um bom trabalho de coleta de informações, é muito menos provável que tenha que rever suas decisões.

Isso realmente ressoou em mim: o processo de proposta Go (como grandes projetos de código aberto geralmente) é um sistema com uma carga oferecida muito mais alta do que a capacidade de trabalho, então é importante que nós (discordemos se necessário e) nos comprometamos e passemos para a próxima decisão .

string e [] byte foram adicionados bem tarde no processo de consideração desse problema, em resposta ao feedback inicial, e claramente não pensamos em todas as implicações disso. Então, apresentei essas duas novas edições, # 43216 e # 43217.

Por outro lado, a sintaxe // go: embed foi uma parte central das discussões originais e foi amplamente discutida, tanto prós quanto contras. Não acredito que haja “novas informações significativas” que devam nos levar a reconsiderar essa sintaxe inteiramente, então, no interesse de seguir em frente e manter o conselho de Ousterhout sobre reconsideração em mente, estou deixando isso de lado.

Obrigado novamente por apontar os problemas de string e [] bytes!

Evidentemente. O compilador Go organiza especificamente para string e embed.FS incorporações a serem desduplicadas e colocadas em rodata, enquanto []byte incorporações não são:

@rsc por acaso você pensou neste último ponto? Com a implementação atual, pode se tornar uma "prática recomendada" usar string em vez de [] byte para produzir binários melhores. Estamos bem com isso?

Não entendo por que isso seria uma "prática recomendada". Para mim, isso é o mesmo que dizer que é "prática recomendada" fazer constantes de erros sentinela, portanto, não devemos permitir variáveis ​​com escopo de pacote de erro de tipo - no sentido de que eu discordo de ser uma boa prática e da restrição adicional como um solução.

Eu pude ver um argumento para usar apenas string em vars locais. Mas em variáveis ​​com escopo de pacote, a semântica é clara e bem definida e eu não recomendaria não usar um embed []byte mais do que eu recomendaria não usar qualquer outra variável []byte .

@mvdan , se as pessoas string vez de []byte como uma prática recomendada, eu consideraria isso uma coisa boa. string é o tipo Go apropriado para “uma sequência imutável de bytes”, enquanto []byte é o tipo apropriado para “uma sequência mutável de bytes” ou “uma sequência de bytes de mutabilidade indeterminada”.

Para os casos em que você tem um valor do tipo string (imutável) e deseja usá-lo como tipo []byte (indeterminado), você já pode usar unsafe para fazer isso corretamente . (Veja, por exemplo, meu unsafeslice.OfString ). Talvez devêssemos adicionar uma biblioteca padrão com suporte para essa operação, mas isso parece uma proposta separada.

Portanto, parece correto usar sempre o tipo string se você realmente deseja que o valor seja somente leitura.

@Merovius @bcmills, você levanta bons pontos e, para ficar claro, não tenho objeções. Eu só quero ter certeza de que os designers da proposta pensem sobre essa distinção antes do lançamento final.

Eu realmente não acho que a desduplicação surgirá muito na prática. (Em que configuração vários pacotes irão incorporar exatamente os mesmos arquivos?) As pessoas devem usar a forma que precisam e não se preocupar com "string significa que meus binários são menores", porque em geral não acho que isso seja verdade.

Algumas pessoas perguntaram sobre ETags. Ficamos sem tempo para isso, mas apresentei uma proposta em https://github.com/golang/go/issues/43223 e espero que isso leve a uma boa ideia que pode entrar no Go 1.17. Desculpe por não ter conseguido nesta rodada.

Obrigado por apresentar os números 43216 e 43217. Se forem aceitos, eles resolverão substancialmente minhas preocupações pendentes com a proposta //go:embed .

Por outro lado, a sintaxe // go: embed foi uma parte central das discussões originais e foi amplamente discutida, tanto prós quanto contras. Não acredito que haja “novas informações significativas” que devam nos levar a reconsiderar essa sintaxe inteiramente, então, no interesse de seguir em frente e manter o conselho de Ousterhout sobre reconsideração em mente, estou deixando isso de lado.

Posso respeitar a vontade de não querer revisitar assuntos que já foram decididos por meio de extensa discussão. Mas depois de revisar a discussão que ocorreu no # 35950, o tópico do //go:embed .

Aqui estão os comentários que descobri que tocaram na sintaxe para indicar quais arquivos incorporar:


18 Problema do GitHub e comentários do Reddit

  • https://github.com/golang/go/issues/35950#issuecomment -561443566 "A abordagem //go:embed introduz outro nível de complexidade também. Você teria que analisar os comentários mágicos para verificar o código. A abordagem do "pacote embutido" parece mais amigável para a análise estática. " (Observação: a proposta //go:embed revisada torna mais fácil digitar o código de verificação, mas ainda não é trivial se um analisador quiser realmente ver as strings usadas, porque elas não estão em go / ast.)
  • https://github.com/golang/go/issues/35950#issuecomment -561450136 "Esse é um argumento muito forte para usar um pacote. Também o torna mais legível e documentável, uma vez que podemos documentar tudo com godoc regular, em vez do que nos documentos do cmd / go. " (Observação: acho que isso é abordado principalmente por # 43217, pois os tipos de incorporação de pacote fornecem um ponto de referência fácil.)
  • https://github.com/golang/go/issues/35950#issuecomment -561840094 "Existe um motivo pelo qual não poderia fazer parte do go build / link ... por exemplo, go build -embed example=./path/example.txt e algum pacote que expõe acesso a ele (por exemplo, embed.File("example") , em vez de usar go:embed ? "(Sugere sintaxe de função sobre a diretiva //go: . Downvoted, mas suspeito porque sugeriu go build bandeiras.)
  • https://github.com/golang/go/issues/35950#issuecomment -561726107 "Não sou particularmente fã de um comentário que compila para código, mas também acho que o pseudo-pacote que afeta a compilação é um pouco estranho também. Se a abordagem de diretório não for usada, talvez faça um pouco mais sentido ter algum tipo de declaração de nível superior incorporada na linguagem. Funcionaria de forma semelhante para importar, mas só suportaria caminhos locais e exigiria um nome para ser atribuído a ele. " (Não gosta de //go: nem de novas funções integradas, mas ainda prefere código em vez de comentários.)
  • https://github.com/golang/go/issues/35950#issuecomment -561856033 "Além disso, uma vez que //go:generate diretivas não são executadas automaticamente na construção go, o comportamento da construção go pode parecer um pouco inconsistente: //go:embed funcionará automaticamente, mas para //go:generate você tem que executar go generate manualmente. ( //go:generate já pode interromper o fluxo go get se ele gerar arquivos .go necessários para a compilação). " (Incluído para completar, mas já temos //go: diretivas que afetam e não afetam go build comportamento mesmo sem //go:embed , portanto, não acho este argumento convincente.)
  • https://github.com/golang/go/issues/35950#issuecomment -562005821 "Eu votaria no novo pacote em oposição à diretiva. Muito mais fácil de controlar, mais fácil de manusear / gerenciar e muito mais fácil para documentar e estender. Por exemplo, você pode encontrar facilmente a documentação para uma diretiva Go, como “go: generate”? E quanto à documentação para o pacote “fmt”? Você vê para onde estou indo com isso? " (Novamente, principalmente abordado pelo # 43217.)
  • https://github.com/golang/go/issues/35950#issuecomment -562200553 "Um argumento a favor de um pragma de comentário (// go: embed) em vez de um nome de diretório especial (estático /): um comentário nos permite incorpore um arquivo no arquivo de teste de um pacote (ou no arquivo xtest), mas não na biblioteca em teste. O comentário só precisa aparecer em um arquivo _test.go. " (Observação: o argumento é contra o diretório especial, não para //go:embed especificamente; o mesmo argumento se estende a embed.Files funções, etc.)
  • https://github.com/golang/go/issues/35950#issuecomment -562235108 "Gosto da abordagem package embed para usar a sintaxe Go"
  • https://github.com/golang/go/issues/35950#issuecomment -562713786 "A abordagem import "C" já estabeleceu um precedente para caminhos de importação" mágicos ". IMO funcionou muito bem."
  • https://github.com/golang/go/issues/35950#issuecomment -562768318 "Várias pessoas falaram sobre fornecer automaticamente uma API para ler os arquivos incorporados, em vez de precisar de outro sinal, como um comentário mágico. Acho que deveria ser o caminho a seguir, pois oferece uma sintaxe programática familiar para a abordagem. Ir para um pacote especial, possivelmente runtime/embed como mencionado anteriormente, satisfaria isso e permitiria uma fácil extensibilidade no futuro. "
  • https://github.com/golang/go/issues/35950#issuecomment -562959330 "Em vez de reutilizar comentários (que acabam espalhados por todo o lado e, a título pessoal, parecem nojentos), [... ]. " (Continua sugerindo o uso de arquivos go.mod para enumerar arquivos incorporados. Para ser justo, também argumenta "Isso contorna questões como ter que restringir o pacote mágico para permitir apenas strings literais como argumentos, mas significa que é mais difícil verificar se os ativos incorporados são realmente usados ​​em qualquer lugar ou acabam sendo um peso morto. ")
  • https://github.com/golang/go/issues/35950#issuecomment -562966654 "Mais uma ideia: em vez de adicionar um novo tipo de verbo embed em go.mod, poderíamos introduzir um novo tipo de pacote, um pacote de dados , que é importado e usado em go.mod da maneira usual. Aqui está um esboço de espantalho. " (Preferência por código em vez de comentários.)
  • https://github.com/golang/go/issues/35950#issuecomment -563156010 "Aqui está algo que ainda não foi mencionado: a API para ferramentas de processamento de origem Go (compilador, analisadores estáticos) é tão importante quanto a API de tempo de execução . Este tipo de API é um valor fundamental do Go que ajuda a desenvolver o ecossistema (como go/ast / go/format e go mod edit ). [...] No caso de um pacote especial, não vejo nada a mudar em go.mod parsing ( go mod tools) ou go/ast parser. "
  • https://github.com/golang/go/issues/35950#issuecomment -601748774 Sugere a listagem de arquivos de recursos go.res. Usa código, não comentários.
  • https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fyv9gxw/ "Por que precisamos do // go: embed comment eg binclude faz binclude.Include(filename) para incluir um arquivo / diretório o que cerca de fs.Embed(filename) ? "
  • https://github.com/golang/go/issues/41191#issuecomment -686625127 "Na minha opinião, adicionar açúcar sintático na linguagem para oferecer suporte a essa mudança de ferramenta é uma mudança de linguagem. Tenho certeza de que isso é óbvio para os outros, mas isso é efetivamente um comentário como código. Eu sinto fortemente que magic / sugar diminui a simplicidade e legibilidade da linguagem; é muito fácil perder um comentário mágico que incorpora um arquivo. "
  • https://github.com/golang/go/issues/41191#issuecomment -690423900 "Tenho preocupações semelhantes às de @tristanfisher . Go tem usado comentários mágicos como diretivas de compilador há muito tempo (desde o início?), mas eles destinam-se a casos extremos e é raro que apareçam no código. Dada a popularidade da incorporação de conteúdo estático em binários Go, //go:embed provavelmente será mais comum. Talvez seja hora de considerar uma sintaxe diferente para o compilador diretivas? "
  • https://github.com/golang/go/issues/41191#issuecomment -690522509 "Compartilho suas preocupações sobre comentários mágicos."

Conforme leio os comentários, eles parecem sempre favorecer a sintaxe Go, com a ressalva de querer evitar alterações que quebrem as ferramentas. Eu não vi ninguém discutindo para //go:embed como a ortografia, tanto quanto apenas aceitá-lo como é. Adicionar novos intrínsecos do compilador com stubs de compatibilidade com versões anteriores para verificadores de tipo legado parece mais em linha com a preferência expressa dos participantes da discussão do que mais //go: diretivas.

No estilo das pesquisas de polegar para cima / polegar para baixo em # 43216 e # 43217:

  • Gostei (👍) se você preferir novos intrínsecos do compilador (por exemplo, CL 276835 ):

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

    Veja embed_test.go para mais exemplos de uso.

    (Observação: os argumentos devem ser literais de string , não apenas valores de string. Ou seja, constantes de string declaradas como const logo = "logo.jpg" ou expressões de string constantes como "logo" + ".jpg" também não são permitidas. No entanto, embed.Files , etc. pode ser usado em qualquer contexto, não apenas para inicializar variáveis.)

  • Não gostei (👎) se você preferir novas diretivas de compilador (ou seja, a proposta atual):

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

@mdempsky Acho que Russ foi bastante claro, o que seria necessário para justificar uma reversão da decisão para ele - novas informações. Acho que reunir comentários anteriores claramente não é isso. Sem intenção de ofender.

Não há precedentes para esses novos tipos de funções, certo? Ou seja, algo que se parece com uma função de pacote normal, mas na verdade é um tipo especial de embutido que só pode ser chamado de uma determinada maneira?

Você diz "intrínseco", mas os intrínsecos atuais se comportam exatamente como as funções Go normais.

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

Vestir novas diretivas para se parecerem com funções Go, mas com uma sintaxe diferente (mais restritiva) do que as funções Go, parece uma opção ruim de onde estou sentado. Acho que embed precisaria ser descrito na especificação, por um lado, ao contrário das diretivas //go: .

(Você pode aproximar os "únicos argumentos literais permitidos" no código Go normal criando uma função que usa argumentos type internalString string não exportados, mas deduzo que não é isso que você está propondo, já que seu CL faz alterações no analisador e verificador de tipo.)

@Merovius De acordo com o processo de proposta Go:

Consenso e discordância

O objetivo do processo de proposta é chegar a um consenso geral sobre o resultado em tempo hábil.

Se não for possível chegar a um consenso geral, o grupo de revisão de propostas decide a próxima etapa revisando e discutindo a questão e chegando a um consenso entre eles. Se mesmo o consenso entre o grupo de revisão da proposta não puder ser alcançado (o que seria extremamente incomum), o árbitro (rsc @) analisa a discussão e decide a próxima etapa.

Na minha leitura dos comentários, o consenso pareceu favorecer a sintaxe do código Go. Se o consenso agora é de fato ficar com //go:embed , eu respeito isso. Mas não acho que o processo documentado justifique a decisão inicial de avançar com //go:embed .

(No momento, os resultados da pesquisa favorecem fracamente novas funções em vez de novas diretivas, mas não muito. Se os polegares para cima não superarem os polegares para baixo em pelo menos 2: 1, não há problema em simplesmente ignorar isso.)

@cespare

Não há precedentes para esses novos tipos de funções, certo? Ou seja, algo que se parece com uma função de pacote normal, mas na verdade é um tipo especial de embutido que só pode ser chamado de uma determinada maneira?

Existem funções pré-declaradas do universo e funções não seguras do pacote.

Você pode argumentar que o pacote inseguro está documentado na especificação Go, mas eu argumento que não precisa ser. Go costumava oferecer suporte a modos em que o pacote não seguro não estava disponível para os usuários e, ainda hoje, o pacote não seguro tem funcionalidade e restrições que são documentadas apenas nos documentos do pacote, não nas especificações do Go.

Existem também funções internas dentro do tempo de execução Go que são implementadas apenas pelo compilador Go. Por exemplo, getg , getcallerpc , getcallersp e getclosureptr .

(Você pode aproximar os "únicos argumentos literais permitidos" no código Go normal criando uma função que usa argumentos de string internalString do tipo não exportado,

Acho que seria uma adição razoável para restringir ainda mais o comportamento dos verificadores de tipo legado.

mas percebi que não é isso que você está propondo, já que seu CL faz alterações no analisador e no verificador de tipo.)

CL 276835 não altera o analisador, exceto para remover o novo código do analisador adicionado para //go:embed . Isso muda o verificador de tipo, mas comparativamente a //go:embed antes.

Seria fácil estender go / types para estar ciente do pacote embed, mas optei por não fazer para CL 276835 especificamente para mostrar que ele ainda funciona (por exemplo, cmd / vet não está falhando nos testes de unidade do pacote embed).

@mdempsky Você pode discordar sobre se um consenso foi ou não alcançado naquele momento. Assim como você pode discordar da própria decisão. Eu não acho que isso realmente mude as coisas, no entanto. No final das contas, "há consenso" também é uma decisão tomada. E exatamente os mesmos pontos sobre a exigência de novas informações para uma reversão se aplicam a essa decisão.

A necessidade de satisfazer cada pessoa é um vetor de DDoS - tanto no que diz respeito à decisão quanto ao processo com que foi feita.

FTR, a questão sobre ferramentas vs. mudança de linguagem foi discutida , assim como a compensação entre "string-literal vs. string-constant" ("string-literal" requer mágica no verificador de tipo, "string-constant" requer o ir ferramenta para fazer a verificação de tipo - comentários não precisam de nenhuma das duas). Então, novamente, não há realmente nada de novo aqui. Você pode discordar com o resultado de que a decisão ou o processo foi feito com - mas os argumentos que você mencionou têm sido considerados ao tomar a decisão.

A necessidade de satisfazer cada pessoa é um vetor de DDoS - tanto no que diz respeito à decisão quanto ao processo com que foi feita.

Não estou exigindo que esteja pessoalmente satisfeito e acho um insulto que você caracterize minhas postagens como tal. Anteriormente, você também conversou comigo como se eu não estivesse familiarizado com a linguagem Go ou com o compilador Go. Por favor, pare com a condescendência.

Listei acima vários comentários onde as pessoas quase universalmente expressaram uma preferência de não adicionar novas diretivas //go: , enquanto ninguém havia feito comentários afirmativos em seu apoio. Como tal, a esmagadora preferência expressa pela comunidade pareceu-me favorecer a sintaxe Go, e meu comentário estava argumentando em defesa de suas preferências declaradas.

No entanto, como está, https://github.com/golang/go/issues/41191#issuecomment -747095807 tem mais polegares para baixo do que polegares para cima. Isso é surpreendente para mim, porque parece inconsistente com todos os comentários durante a discussão anterior. Mas estou feliz em aceitar que a questão foi abordada diretamente e (especialmente como alguém que estará envolvido no suporte de longo prazo para esse recurso dentro do compilador Go) estou mais feliz agora em apoiar //go:embed visto que é de fato a preferência da comunidade e não apenas a preferência dos autores da proposta. Este resultado não teria sido alcançado se a discussão tivesse sido encerrada, como você parece determinado.

FTR, a questão sobre ferramentas vs. mudança de linguagem foi discutida , assim como a compensação entre "string-literal vs. string-constant" ("string-literal" requer mágica no verificador de tipo, "string-constant" requer o ir ferramenta para fazer a verificação de tipo - comentários não precisam de nenhuma das duas).

Esse comentário é irrelevante para os argumentos que eu estava apresentando. A grafia alternativa que criei no CL 276835 tem exatamente as mesmas propriedades técnicas da grafia //go:embed : as ferramentas que precisam saber quais arquivos devem ser incorporados precisarão ser atualizadas para processar os arquivos de origem Go de maneira diferente (por exemplo, para erro sobre o uso indevido de //go:embed comentários ou da função embutida embed.Bytes ), enquanto as ferramentas existentes podem continuar a processar o código razoavelmente sem se preocupar com eles (por exemplo, go / types irá ignorar //go:embed comentários mas não detectará se é aplicado a tipos de variáveis ​​incorretos e pode verificar o tipo embed.Bytes usando as declarações de stub, mas não saberá rejeitar todas as chamadas que usam argumentos diferentes de literais de string).

A questão de saber se alguma delas é uma "mudança de linguagem" é mais filosófica do que técnica.

(O argumento de Russ de que "se um programa é válido não muda" na proposta //go:embed também está incorreto. Https://github.com/golang/go/issues/41191#issuecomment-747799509 dá um exemplo de um pacote que era e é válido de acordo com as especificações do Go e também foi aceito pelos lançamentos do conjunto de ferramentas Go até o Go 1.15, mas não será mais válido no Go 1.16.)

Como alguém que deu um 👍 à proposta de Matt de usar var website = embed.Files("logo.jpg", "static/*") , minha preocupação em usar o formulário de comentário ( //go:embed logo.jpg static/* ) é "facilidade de uso".

Por exemplo, esses 2 exemplos de programa produziriam 2 coisas diferentes, apenas porque uma "importação" foi perdida:

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
}

Ao forçar o desenvolvedor a usar a importação por meio da semântica da linguagem, você minimiza os problemas em que os arquivos incorporados não estão funcionando conforme o esperado porque não foram inicializados conforme o esperado.

@ kushieda-minori Embora eu ache que minha proposta também seja mais fácil de usar, o primeiro exemplo já não compila na dica:

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

Acho que esse problema também é atenuado por # 43217, já que você precisa importar "embed" para declarar variáveis ​​do tipo embed.Bytes e embed.String qualquer maneira. E o compilador (mas não go / types ou cmd / vet) também já relata um erro se você aplicar a diretiva //go:embed a uma variável de um tipo incorreto.

@mdempsky , entretanto, compilar acidentalmente em uma versão mais antiga do Go não falhará e pode dar uma falsa sensação de que a incorporação funcionou.

Versões mais antigas do Go não têm pacote incorporado, então import "embed" falhará. É verdade que existe o risco de os usuários escreverem:

package p

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

e será silenciosamente aceito pelo Go 1.15 e anteriores. Mas não será aceito pelo Go 1.16 e mais recentes. Não há pelo menos nenhum programa que compile com Go 1.15 e Go 1.16 e tenha semânticas diferentes (devido à incorporação de pacote, pelo menos).

Acho que uma correção (parcial) apropriada aqui seria o cmd / compile parar de aceitar diretivas //go: desconhecidas. Por exemplo, outra limitação da proposta atual relativa à sintaxe embutida Go é que se você digitar incorretamente a diretiva //go:embed (digamos //go:enbed , //go;embed ou // go:embed com um espaço), ele também será ignorado silenciosamente. (Considerando que erros de digitação como enbed.Bytes("foo.txt") causariam um erro de verificação de tipo, mesmo com go / types não modificados.)

Grande observação sobre diretivas go: não validadas. Se isso fosse aplicado, ajudaria a aliviar erros de digitação difíceis de detectar.

Outra ideia que acabei de pensar é que minhas ferramentas estão configuradas para adicionar / remover importações automaticamente, conforme necessário. Se minhas ferramentas estiverem desatualizadas, tenho que evitar a remoção da importação "não utilizada" embed ? Percebi que está resolvido se eu usar embed.String etc, mas usar []byte] normais e string é considerado totalmente válido. Isso pode ser frustrante para um novo gopher que está escolhendo trechos da web para fazer algo funcionar, caso não veja os aliases embed.* .

mas usar []byte] e string normais é suposto ser completamente válido.

Eles não serão se # 43217 for aceito. Eu recomendo a leitura sobre # 43216 e # 43217. Ambos receberam um apoio extremamente positivo até agora e parecem muito prováveis ​​de serem aceitos por mim. (No entanto, não estou no comitê de revisão de propostas.)

Obrigado, quando li # 43217 pela primeira vez, perdi a palavra-chave
"have" para usar os tipos embed.* .

Acho que minha única preocupação é a última que você apontou.

Em Qui, 17 de dezembro de 2020, 20:24 Matthew Dempsky [email protected]
escreveu:

mas usar regular [] byte] e string deve ser completamente válido.

Eles não serão se # 43217 https://github.com/golang/go/issues/43217 for
aceitaram. Eu recomendo a leitura sobre # 43216
https://github.com/golang/go/issues/43216 e # 43217
https://github.com/golang/go/issues/43217 . Ambos receberam
apoio esmagadoramente positivo até agora, e parece muito provável que seja aceito
para mim. (No entanto, não estou no comitê de revisão de propostas.)

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/golang/go/issues/41191#issuecomment-747808153 ou
Cancelar subscrição
https://github.com/notifications/unsubscribe-auth/ADLZABJWBJX475BVYDVD6ODSVKVOTANCNFSM4QTHVTUA
.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

longzhizhi picture longzhizhi  ·  3Comentários

natefinch picture natefinch  ·  3Comentários

dominikh picture dominikh  ·  3Comentários

lkarlslund picture lkarlslund  ·  3Comentários

jayhuang75 picture jayhuang75  ·  3Comentários