Lançamento do Go 1.22
(go.dev)- A variável do loop
foragora passa a ser criada novamente a cada iteração, evitando bugs de compartilhamento não intencional;rangesobre inteiros também passa a ser possível - A compatibilidade mantém a promessa de compatibilidade do Go 1, e espera-se que quase todos os programas Go continuem compilando e executando como antes
- Em workspaces, é possível usar o diretório vendor gerado com
go work vendor; se houver umvendordo workspace,-mod=vendorpassa a ser o padrão - Foi removido o suporte a
go getfora de módulos no modo legadoGOPATHcomGO111MODULE=off; outros comandos de build, comogo buildego test, continuam funcionando para programas legados emGOPATH - O runtime foi alterado para manter metadados de GC baseados em tipo próximos aos objetos no heap, melhorando o desempenho de CPU de programas Go em 1–3% e reduzindo o overhead de memória em cerca de 1% em vários programas
- O alinhamento de endereços de alguns objetos pode mudar do padrão anterior de 16 bytes ou mais para alinhamento de 8 bytes; código em assembly que pressupõe alinhamento maior precisa ser ajustado, e é possível usar o workaround temporário
GOEXPERIMENT=noallocheaders - Builds com PGO agora podem desvirtualizar mais chamadas, e a ativação de PGO melhora o desempenho em tempo de execução em 2–14% em um conjunto representativo de programas Go
- Foi adicionado à biblioteca padrão o primeiro pacote
v2,math/rand/v2, comReadnão herdado, gerador global sempre inicializado com seed aleatória, fontesChaCha8ePCG, e função genéricaN - Os padrões de roteamento de
net/http.ServeMuxagora suportam método e curingas; há pequenas quebras de compatibilidade em padrões com{}e no tratamento de caminhos escapados, e o comportamento anterior pode ser restaurado comGODEBUG=httpmuxgo121=1 - A versão mínima padrão dos servidores
crypto/tls, quando não especificada, passa a ser TLS 1.2, e cipher suites sem ECDHE não são mais oferecidas por padrão em handshakes anteriores ao TLS 1.3 - O tracer de execução foi totalmente reformulado para usar o relógio do SO na maioria das plataformas, oferecer traces segmentados com processamento em streaming, duração completa de syscalls e informações sobre a thread do SO em que cada goroutine foi executada
- A interface web da ferramenta
tracefoi atualizada como parte do suporte ao novo tracer, e traces de programas compilados com Go 1.22 ou superior agora mostram uma visão orientada a threads e a duração completa de syscalls - O
vetajusta os alertas sobre fechamento em loop para a semântica de variáveis de loop do Go 1.22, e adiciona avisos paraappendsem valores,time.Sincenão adiado dentro dedefere incompatibilidade de pares key/value emlog/slog - O bootstrap agora exige pelo menos o último point release do Go 1.20, e espera-se que o Go 1.24 exija pelo menos o último point release do Go 1.22
- Em
darwin/amd64, o toolchain do Go passa a gerar PIE por padrão; o Go 1.22 é a última versão que roda no macOS 10.15 Catalina, e o Go 1.23 exigirá macOS 11 Big Sur ou superior - Foram adicionados ao
go/typeso tipoAliase a funçãoUnalias; comgotypesalias=1, clientes precisam lidar com o tipoAlias slices.Delete,DeleteFunc,Compact,CompactFunceReplaceagora zeram os elementos da região reduzida, eInsertsempre gera panic seiestiver fora do intervalo- Funções de leitura de diretório no Windows agora leem itens em lote para reduzir o número de chamadas de sistema, com melhoria de desempenho de até 30%
1 comentários
Opiniões do Hacker News
Nos últimos 3 anos usei principalmente TypeScript e recentemente comecei a usar Go; no começo fiquei um pouco preocupado pela falta de funções para arrays e pelo sistema de tipos menos flexível
Mas, ao configurar de novo um projeto em TypeScript para uma equipe de 4–5 pessoas, gastou-se tempo demais com linting, escolha de biblioteca de roteamento no servidor, escolha de servidor, padrões de codificação, tratamento básico de erros, impor erros customizados ou um tipo
Resultpara evitar o inferno detry/catchaninhado, testes e mocks, configuração do Prisma etc.Eu gosto de TypeScript, mas é realmente confortável usar uma linguagem com uma ótima biblioteca padrão, segurança de tipos de verdade e padrões de codificação embutidos. Go também tem suas peculiaridades, mas é bem boa, e é ótimo que até o roteamento tenha entrado na biblioteca padrão, porque é uma coisa a menos com que se preocupar. Agora só estou esperando funções
map/filter/findpara slicesEnquanto o TypeScript fica cada vez mais complexo a cada release para atender power users, Go acrescenta coisas que faltavam, como
rangesobre inteiros, e torna o uso mais simplesÉ parecido com sequências de jogos que vão aumentando configurações e mecânicas até precisarem de um reset para novos usuários; em linguagens de programação isso é difícil de fazer, então é bom que Go esteja consciente disso
go:embedego:generateOs pacotes
golang.org/xtambém são ótimos, porque praticamente qualquer implementação relacionada a RFCs da internet já vem pronta para usomap/filter/findpara slices, dá para usar a biblioteca lo, inspirada no lodashÉ meu canivete suíço favorito em projetos Go: https://github.com/samber/lo
Era um ciclo recorrente: você lia a documentação para fazer algo e logo alguém aparecia contando um truque utilizável que não estava na documentação
Se as notas oficiais de lançamento parecerem um pouco secas, fiz uma versão interativa: https://antonz.org/go-1-22
fornão ficou claro para mim de início, mas ajudou executar diretamente e ver um resultado inesperadoCompacteReplacesão confusos; não sei se estão certosUso Go há mais de 9 anos e, nos últimos 4, usei bastante Dart por causa do Flutter; vejo as duas linguagens como tendo atitudes totalmente opostas em relação à complexidade. Dart tenta adicionar todos os recursos possíveis, enquanto Go faz o oposto
Primeiro, em Dart há maneiras demais de inicializar valores dentro de classes, o que faz perder muito tempo. Dependendo dos prefixos
final/const/static/late, mudam os lugares de inicialização — construtores, factories,initState()de umStatefulWidgetdo Flutter etc. — e, ao refatorar, é preciso mexer bastante na inicialização. Getters em que funções parecem variáveis também aumentam a confusão em uma base de código novaSegundo, Dart tem muitos recursos que são bons para enfiar mapas, streams etc. em uma única linha, o que dá vontade de criar rapidamente código de uma linha difícil de entender. Mas, ao depurar, ou quando um desenvolvedor júnior encontra hacks como
firstWhereOrNulle parâmetros opcionais comoorElse: () => null, a confusão é grande. Muitas vezes uma null safety ou um erro de tipo obscuro também trava tudo; transformar isso em um simples loopforno estilo Go dá um verdadeiro alívioinitStatepara coisas que não estejam diretamente relacionadas à UI. Eu costumo evitar, a menos que seja algo como definir a dica de um campo de textoNa maioria das páginas Flutter há muito pouco a fazer, ou então o pai cria um objeto
MyPageController, inicializa como quiser e faz a página filha se comportar de acordo com esse controlador. Por exemplo, em uma página de lista, quando o usuário quer editar um item, você cria um controlador, coloca o item nele e envia para a página filha; quando ela retorna, o pai pode olhar o item do controlador ou suas variáveis/callbacks para decidir mudanças na UIAssim dá para controlar finamente a interação entre widgets sem mexer em coisas como
InheritedWidget. Claro que é bom usar isso junto com uma biblioteca de gerenciamento de estado, mas, na prática, muitas vezes isso não é usadorangeé parecido: no fim, a quantidade de caracteres digitados é semelhante à de uma compreensão comoforEach(f), só parece maior porque fica espalhada por três linhas; ao expandir manualmente, não é nada demaisDito isso, em relação à inicialização, eu gostaria que Go tivesse uma forma idiomática de inicializar campos de structs com valores específicos. Tudo bem não haver construtores, mas às vezes eu queria que um
boolfosse inicializado comotruepor padrãoinitStateé uma sobrescrita deStatefulWidgete não é diferente de eventos de ciclo de vida de outros frameworks; é um conceito do Flutter, então não é algo que deva ser confundido com construtoresPode haver confusão sobre usar um construtor normal ou uma factory, mas é difícil dizer que Go é superior por causa de
initStateAo copiar de
TCPConnparaUnixConn,io.Copypassa a usar, quando possível, a chamada de sistemasplice(2)do Linux e o novo métodoTCPConn.WriteToCom o upgrade de interface, tivemos mais um aumento transparente de I/O sem cópia. O efeito obtido com esse padrão no pacote
ioé realmente enormeAinda assim, está dentro do esperado
Os padrões de roteamento aprimorados são uma boa melhoria agora que o futuro do Gorilla Mux está instável
No entanto, essa mudança quebra a compatibilidade retroativa de pequenas formas: há partes claras, como a mudança no comportamento de padrões com
{e}, e partes menos óbvias, como o tratamento de caminhos escapados. Isso é controlado pelo campohttpmuxgo121deGODEBUG; definindohttpmuxgo121=1, é possível voltar ao comportamento anteriorMas fico curioso para saber por que isso não entra em conflito com a promessa de compatibilidade do Go 1: “programas escritos para a especificação Go 1 continuarão compilando e executando corretamente, sem alterações, durante a vida útil dessa especificação”
gonogo.modSe for 1.22 ou superior, você recebe o novo comportamento da biblioteca; caso contrário, recebe o comportamento antigo. Resta ver quão bem isso vai funcionar na prática
{e}em caminhos de handlers HTTPEles também oferecem uma forma de desativar o novo comportamento, então não forçam mudanças no código, embora seja preciso definir uma nova variável de ambiente
A mudança de semântica do loop
fornesta versão é um exemplo parecido: na prática, é uma mudança que quebra compatibilidade. Mesmo assim, todos os programas Go continuam compilando e executando; apenas alguns comportamentos mudam um pouco. Acho que o Go adotou uma abordagem pragmática, e isso foi uma das razões do seu sucessogo.modpara quebrar por versão, então pelo menos deveriam quebrar de uma forma melhorQuando se muda o
go.modpara 1.22, seria muito melhor o código falhar na compilação do que ter de encontrar problemas sutis em tempo de execuçãoNo Go 1.22, cada iteração de um loop passa a criar uma nova variável
Isso já foi discutido aqui antes: https://news.ycombinator.com/item?id=33160236 - Go: Redefining For Loop Variable Semantics (2022)
Posso estar sendo antiquado, mas não gosto da adição de
rangesobre funções. A conveniência não é grande o bastante para justificar a complexidade acrescentada à linguagem, e o estilo funcional parece destoar do estilo explícito, imperativo e verboso, porém enxuto em recursos, do Go. Acho que isso era uma das grandes vantagens do GoPelo mesmo motivo, também acho que
rangesobre inteiros vai na direção errada. O conjunto enxuto de recursos do Go e sua baixa carga cognitiva sempre foram grandes pontos fortes e diferenciaisUm dos grandes problemas no desenvolvimento de software é impedir que desenvolvedores adicionem complexidade ou tentem demonstrar habilidade. Especialmente no início da carreira, desenvolvedores tendem a complicar demais a solução, em vez de resolver o problema de forma direta, ainda que menos elegante e mais verbosa, e seguir em frente
O suporte atual a genéricos no Go não é sofisticado o suficiente para sustentá-lo, então surgem limitações estranhamente forçadas
A adição de
sql.Null[T]é boa. Em projetos novos, provavelmente vou começar a usarNo trabalho atual, dependo do null do sqlboiler [0], cuja API é muito parecida. Ele funciona como
sql.Null, mas tem um método adicionalIsSet() bool, que informa se o valor já foi definido explicitamente, ajudando a distinguir entre “null intencional” e “null porque não foi inicializado”. Parece bom, mas na prática nunca useiEstou ansioso para revisitar o mux da biblioteca padrão e ver se talvez dá até para remover o chi. É muito bom ver esse tipo de funcionalidade entrando na biblioteca padrão
Quando esse tipo de funcionalidade entra na biblioteca padrão, ela passa a ser sempre mantida e garante que muitos programas Go usem a mesma abordagem
Para quem usa Go em produção, fico curioso se dá para migrar rapidamente para a versão mais recente ou se vocês ficam presos a releases antigos
Olhando projetos públicos, parece que muita gente evita recursos relativamente novos. Mesmo sem usar genéricos, eu gostaria de usar
any, introduzido no Go 1.18, em vez do antigointerface{}; já ouvi dizer até que o primeiro é mais “idiomático”Desta vez ficou um pouco estranho: a equipe do Go lançou o 1.22 antes de atualizarmos o padrão para 1.21. Hoje, esperar é quase uma tradição, e é raro um release do Go quebrar alguma coisa
Por exemplo, esperei cerca de 6 meses antes de realmente usar genéricos, para verificar a utilidade, a experiência de desenvolvimento e se não havia grande impacto na velocidade de build e testes
Mas também entregamos apps para usuários finais em Go, e esses precisam ficar no 1.20 por causa da compatibilidade com sistemas operacionais de clientes antigos. Como houve um corte considerável no 1.21, é bem provável que fiquemos no 1.20 por alguns anos
latestdo Go, e a suíte de testes é bem robusta, então na prática a atualização é automática. Se algo não bater, o pipeline quebra, e então decidimos se fixamos na última versão que funcionou ou se adiamos a atualização até corrigir a mudança que quebrouUma vez a cada alguns anos, uma nova versão revela algum bug sutil de caso de borda em interação com um pacote de terceiros, mas esse tipo de bug, por definição, escapou da suíte de testes, então provavelmente também não teria sido detectado em um upgrade manual
Se a preocupação for dar suporte a compiladores Go antigos, dá para usar condições de build para versões antigas e definir o que estiver faltando. Por exemplo, colocar
type any = interface{}sob// +build !go1.7