1 pontos por GN⁺ 2024-02-08 | 1 comentários | Compartilhar no WhatsApp
  • A variável do loop for agora passa a ser criada novamente a cada iteração, evitando bugs de compartilhamento não intencional; range sobre 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 um vendor do workspace, -mod=vendor passa a ser o padrão
  • Foi removido o suporte a go get fora de módulos no modo legado GOPATH com GO111MODULE=off; outros comandos de build, como go build e go test, continuam funcionando para programas legados em GOPATH
  • 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, com Read não herdado, gerador global sempre inicializado com seed aleatória, fontes ChaCha8 e PCG, e função genérica N
  • Os padrões de roteamento de net/http.ServeMux agora 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 com GODEBUG=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 trace foi 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 vet ajusta os alertas sobre fechamento em loop para a semântica de variáveis de loop do Go 1.22, e adiciona avisos para append sem valores, time.Since não adiado dentro de defer e incompatibilidade de pares key/value em log/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/types o tipo Alias e a função Unalias; com gotypesalias=1, clientes precisam lidar com o tipo Alias
  • slices.Delete, DeleteFunc, Compact, CompactFunc e Replace agora zeram os elementos da região reduzida, e Insert sempre gera panic se i estiver 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

 
GN⁺ 2024-02-08
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 Result para evitar o inferno de try/catch aninhado, 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/find para slices

    • Em uma newsletter para CTOs, compararam recentemente os lançamentos de TypeScript e Go, usando Go 1.22 como exemplo
      Enquanto o TypeScript fica cada vez mais complexo a cada release para atender power users, Go acrescenta coisas que faltavam, como range sobre 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
    • Sinto algo parecido. O que me fez ficar em Go foi a cadeia de ferramentas de build surpreendentemente unificada, e sempre fico impressionado, ao ver em outros projetos, com o que dá para fazer com go:embed e go:generate
      Os pacotes golang.org/x também são ótimos, porque praticamente qualquer implementação relacionada a RFCs da internet já vem pronta para uso
    • Até chegarem map/filter/find para slices, dá para usar a biblioteca lo, inspirada no lodash
      É meu canivete suíço favorito em projetos Go: https://github.com/samber/lo
    • Por não ter uma quantidade enorme de recursos, acaba reduzindo as pequenas decisões e, contra a intuição, é bastante produtivo
    • Trabalhei por um tempo em uma empresa cliente que usava TypeScript e, depois, passei a chamar aquilo de “Tricks Driven Development”
      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

    • O problema de compartilhamento de variáveis no loop for não ficou claro para mim de início, mas ajudou executar diretamente e ver um resultado inesperado
    • Os exemplos de Compact e Replace são confusos; não sei se estão certos
  • Uso 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 um StatefulWidget do 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 nova
    Segundo, 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 firstWhereOrNull e parâmetros opcionais como orElse: () => 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 loop for no estilo Go dá um verdadeiro alívio

    • Como dica de Dart, é melhor não usar initState para coisas que não estejam diretamente relacionadas à UI. Eu costumo evitar, a menos que seja algo como definir a dica de um campo de texto
      Na 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 UI
      Assim 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 é usado
    • Concordo que a simplicidade de Go é sua maior vantagem. Com range é parecido: no fim, a quantidade de caracteres digitados é semelhante à de uma compreensão como forEach(f), só parece maior porque fica espalhada por três linhas; ao expandir manualmente, não é nada demais
      Dito 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 bool fosse inicializado como true por padrão
    • initState é uma sobrescrita de StatefulWidget e 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 construtores
      Pode haver confusão sobre usar um construtor normal ou uma factory, mas é difícil dizer que Go é superior por causa de initState
    • Devemos otimizar a capacidade de especialistas caros se comunicarem de forma concisa entre si. Tirar ferramentas sintáticas poderosas das mãos de iniciantes só impede que eles desenvolvam especialização
  • Ao copiar de TCPConn para UnixConn, io.Copy passa a usar, quando possível, a chamada de sistema splice(2) do Linux e o novo método TCPConn.WriteTo
    Com 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 enorme

    • A biblioteca padrão de Go tem muitos ajustes desse tipo. Não é necessariamente ruim, mas esse tratamento especial não é extensível, então a integração com bibliotecas não padrão acaba sendo menos satisfatória
      Ainda 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 campo httpmuxgo121 de GODEBUG; definindo httpmuxgo121=1, é possível voltar ao comportamento anterior
    Mas 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”

    • A forma de contornar a promessa de compatibilidade do Go 1 é associar esse tipo de mudança incompatível com versões anteriores ao valor da diretiva go no go.mod
      Se 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
    • Normalmente, a equipe do Go vasculha o GitHub e programas open source antes de quebrar algo, para ver como as pessoas usam aquilo. Provavelmente concluíram que quase ninguém usa { e } em caminhos de handlers HTTP
      Eles 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 for nesta 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 sucesso
    • Vejo isso como um erro raro no Go. Se vão usar o go.mod para quebrar por versão, então pelo menos deveriam quebrar de uma forma melhor
      Quando se muda o go.mod para 1.22, seria muito melhor o código falhar na compilação do que ter de encontrar problemas sutis em tempo de execução
    • Caso contrário, perde-se um slogan curto e forte. Pequena ou não, é uma mudança incompatível
  • No 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 range sobre 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 Go
    Pelo mesmo motivo, também acho que range sobre inteiros vai na direção errada. O conjunto enxuto de recursos do Go e sua baixa carga cognitiva sempre foram grandes pontos fortes e diferenciais

    • No momento, esse recurso é opcional e experimental, e precisa ser ativado explicitamente. Se ele vier a ser ativado por padrão, eu gostaria que houvesse um mecanismo para desligá-lo, para orientar desenvolvedores a não usá-lo quando não for algo comum
      Um 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
    • Esse recurso parece prematuro. Idealmente, deveria vir depois da introdução de lambdas e de packs de parâmetros genéricos
      O suporte atual a genéricos no Go não é sofisticado o suficiente para sustentá-lo, então surgem limitações estranhamente forçadas
    • Também fico em cima do muro. Em comparação com passar funções inline, talvez nem reduza o número de linhas de código
  • A adição de sql.Null[T] é boa. Em projetos novos, provavelmente vou começar a usar
    No trabalho atual, dependo do null do sqlboiler [0], cuja API é muito parecida. Ele funciona como sql.Null, mas tem um método adicional IsSet() 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 usei

    • Fico me perguntando quando seria necessário saber se um null foi intencional. O banco de dados não tem um tipo correspondente para isso, não é?
  • Estou 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

    • Vim dizer exatamente isso. O chi foi excelente em alguns projetos e, na verdade, era tão natural que eu até esquecia que ele existia
      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 antigo interface{}; já ouvi dizer até que o primeiro é mais “idiomático”

    • Na Square, normalmente esperamos o primeiro point release antes de atualizar a versão padrão do monorepo Go. Donos de aplicações individuais podem atualizar antes
      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
    • Costumo esperar para experimentar recursos novos até ver um problema em que eles se encaixem bem, mas a versão do compilador eu atualizo quase imediatamente após o release
      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
    • No trabalho, para contêineres em produção, ou seja, serviços SaaS/API, os desenvolvedores têm liberdade para atualizar para builds mais recentes
      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
    • Nosso CI compila e testa usando o contêiner latest do 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 quebrou
      Uma 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
    • Atualizo na hora
      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