O Go 1.27 acrescenta cinco novos pacotes à biblioteca padrão (encoding/json/v2, encoding/json/jsontext, crypto/mldsa, uuid e simd) e três alterações à linguagem. Algumas novidades vais querer usar logo no primeiro dia, como os métodos genéricos e um pacote uuid que elimina uma dependência da maioria dos ficheiros go.mod. Outras vão fazer falhar a tua suite de testes antes de saberes que a funcionalidade existe: o compress/flate produz bytes diferentes, as funções literais recebem nomes de símbolo diferentes e um punhado de válvulas de escape GODEBUG passa a fazer falhar o build em vez de repor o comportamento antigo.
Índice
- Métodos genéricos e a única coisa que ainda não conseguem fazer
- Um pacote uuid na biblioteca padrão
- Precisas de mudar alguma coisa por causa do encoding/json/v2?
- Assinaturas pós-quânticas com crypto/mldsa
- O pacote experimental simd
- Desempenho que ganhas só por atualizar
- Encontrar fugas de goroutines e ler tracebacks com etiquetas
- Pequenas melhorias na linguagem e na biblioteca
- O que mudou na toolchain do Go?
- Testes e net/http
- O que deixa de funcionar quando atualizas para o Go 1.27?
- Perguntas frequentes
- Fontes
- Continua a aprender
Métodos genéricos e a única coisa que ainda não conseguem fazer
Desde que os genéricos chegaram no Go 1.18, os parâmetros de tipo funcionaram em exatamente dois sítios: funções de nível superior e declarações de tipos. Os métodos ficaram de fora, por isso, se querias uma transformação que mudasse o tipo dos elementos da tua coleção, tinhas de a escrever como função ao nível do pacote, e as funções ao nível do pacote não se encadeiam.
Provavelmente já escreveste algo assim. Um pipeline de métricas que transforma leituras de sensores em rótulos para apresentar precisa de duas transformações, e cada uma tem de envolver a chamada anterior:
example.gogotype Metric struct { Sensor string Celsius int } type List[T any] []T // Package-level, because a method couldn't declare U. func Map[T, U any](l List[T], f func(T) U) List[U] { out := make(List[U], len(l)) for i, v := range l { out[i] = f(v) } return out } func main() { metrics := List[Metric]{ {Sensor: "cpu0", Celsius: 61}, {Sensor: "cpu1", Celsius: 58}, } temps := Map(metrics, func(m Metric) int { return m.Celsius }) labels := Map(temps, func(c int) string { return fmt.Sprintf("%dC", c) }) fmt.Println(labels) // [61C 58C] }
No Go 1.27, Map passa a ser um método a sério que declara o seu próprio parâmetro de tipo, independente do recetor:
example.gogo// U is the method's own type parameter, new in Go 1.27. func (l List[T]) Map[U any](f func(T) U) List[U] { out := make(List[U], len(l)) for i, v := range l { out[i] = f(v) } return out } func main() { metrics := List[Metric]{ {Sensor: "cpu0", Celsius: 61}, {Sensor: "cpu1", Celsius: 58}, } labels := metrics. Map(func(m Metric) int { return m.Celsius }). Map(func(c int) string { return fmt.Sprintf("%dC", c) }) fmt.Println(labels) // [61C 58C] }
O encadeamento é a melhoria óbvia. Os nomes são a menos óbvia: MapSlice, MapSet e MapBox reduzem-se a um único Map por tipo, porque o recetor já diz a que tipo te referes. O recetor também ajuda o compilador: List[Metric] fixa T, por isso só falta inferir o tipo de saída. Além disso, os métodos aparecem quando escreves um ponto no editor. Uma função auxiliar ao nível do pacote só ajuda se já souberes que ela existe.
A própria biblioteca padrão usa a funcionalidade. O math/rand/v2 declara agora (*Rand) N[Int intType](Int) Int como método, ao lado da função N ao nível do pacote, algo que as regras antigas não permitiam.
Há um limite, e é fácil não dar por ele. Das notas de lançamento:
Por isso, isto não compila:
example.gogotype Transformer interface { // Compile error: interface methods can't declare type parameters. Map[U any](f func(Metric) U) any }
O despacho de interfaces acontece em runtime, por isso o compilador não consegue saber que instanciações de um método genérico deve gerar para uma chamada despachada dinamicamente. Se a tua API assenta numa interface, as transformações continuam a precisar da antiga função ao nível do pacote. Saber se os métodos genéricos ajudam a tua base de código depende sobretudo disto, por isso verifica as tuas interfaces antes de planeares um refactor. Se quiseres praticar, o curso Go Generics Masterclass cobre restrições e inferência com exercícios no navegador.
Veredicto: adota já, para tipos concretos. Deixa em paz as APIs baseadas em interfaces.
Um pacote uuid na biblioteca padrão
Quase todos os serviços Go que falam com uma base de dados importam github.com/google/uuid. O Go 1.27 acrescenta um pacote à biblioteca padrão com o caminho de import "uuid", na sequência da proposta #62026.
UUID é definido como [16]byte, o que significa que os valores são comparáveis com == e podem ser usados diretamente como chaves de map. Há três geradores:
example.gogopackage main import ( "fmt" "uuid" ) func main() { fmt.Println(uuid.New()) // reach for this when you don't care how it's made fmt.Println(uuid.NewV4()) // 128 bits, 122 of them random fmt.Println(uuid.NewV7()) // 48-bit timestamp first, so values sort by creation time requestID, err := uuid.Parse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6") if err != nil { return } fmt.Println(requestID, requestID == uuid.Nil()) // false, it parsed fine }
New é a predefinição e atualmente devolve um V4. NewV4 é totalmente aleatório, por isso ninguém consegue adivinhar o seguinte. É isso que queres num ID de pedido público. NewV7 coloca um timestamp de 48 bits no início, por isso os valores mais recentes ficam ordenados depois dos mais antigos, o que os torna boas chaves primárias, porque as inserções vão parar perto do fim do índice em vez de ficarem espalhadas por ele.
Repara que Nil() e Max() são funções, por isso a comparação é id == uuid.Nil(), com parênteses. Nil() serve de valor sentinela para «ainda não definido». Max() é o valor com todos os bits a um, que dá jeito como limite superior quando percorres um intervalo de chaves V7 ordenadas por tempo.
Veredicto: adota já em código novo. Migrar um serviço existente é um localizar e substituir, mas lê as perguntas frequentes antes de removeres a dependência.
Precisas de mudar alguma coisa por causa do encoding/json/v2?
Não. Era esse o objetivo do design. O encoding/json passa a assentar na implementação da v2, e as notas de lançamento são explícitas: «o comportamento de marshaling e unmarshaling mantém-se, mas o texto exato das mensagens de erro pode ser diferente». Tens a velocidade do novo motor sem alterar código nenhum. Segundo as notas de lançamento, «o desempenho do marshal está, de um modo geral, ao nível da implementação anterior, enquanto o desempenho do unmarshal é significativamente mais rápido».
Só adotas o novo comportamento se importares encoding/json/v2 pelo nome. Esse pacote tem predefinições mais rigorosas, escolhidas para corresponder ao que outras implementações de JSON esperam: rejeita UTF-8 inválido em strings JSON, rejeita nomes duplicados dentro de um objeto JSON, faz a correspondência dos nomes dos campos com distinção entre maiúsculas e minúsculas, serializa um slice nil como [] em vez de null e serializa maps por uma ordem não determinista, quando a v1 garantia uma ordem determinista. A mudança no slice nil é a distinção entre nil e vazio de var vs make em Go, e com a v2 consegues vê-la na saída.
example.gogoimport "encoding/json/v2" type Event struct { ID string `json:"id"` Action string `json:"action"` } // v1 would match the key "ID" against the tag `json:"id"`. v2 won't, // and it skips the mismatched key silently rather than erroring. var e Event json.Unmarshal([]byte(`{"ID":"evt_01H","action":"checkout"}`), &e) fmt.Println(e.ID, e.Action) // "" checkout // v2 marshals maps in a non-deterministic order. Ask for a stable one // explicitly when you hash or snapshot the output. inventory := map[string]int{"widget": 12, "gadget": 3, "gizmo": 7} b, _ := json.Marshal(inventory, json.Deterministic(true))
Para recuperar um comportamento da v1 na v2, não trocas de import. Passas uma opção para esse comportamento: Deterministic(true) para uma ordem estável nos maps, MatchCaseInsensitiveNames(true) para uma correspondência de campos flexível, FormatNilSliceAsNull(true) para null em vez de []. O pacote v1 ganhou as mesmas opções, por isso podes adotar a semântica da v2 um comportamento de cada vez, sem uma migração completa. A lista completa está em Migrating to v2, na documentação do pacote v1.
O terceiro pacote, encoding/json/jsontext, trata da sintaxe de nível mais baixo. Expõe o JSON como uma sequência de tokens e valores, com uma máquina de estados que mantém a saída válida. Os codecs de streaming assentam nele. A maioria do código de aplicação nunca o importa diretamente.
Para decidir qual importar:
flowchart TD A["Which JSON import?"] --> B{"Upgrading existing code?"} B -- Yes --> C["encoding/json<br/>v2 engine, v1 behavior"] B -- No --> D{"Want strict defaults<br/>and faster decoding?"} D -- No --> C D -- Yes --> E["encoding/json/v2"] E --> F{"Need raw tokens<br/>or streaming syntax?"} F -- Yes --> G["encoding/json/jsontext"] F -- No --> H["You're done"]
Árvore de decisão para escolher entre encoding/json, encoding/json/v2 e encoding/json/jsontext no Go 1.27.
Veredicto: adota já a predefinição (já o fizeste), adota a v2 pelo nome mais tarde, depois de leres as notas de migração relativas às tuas struct tags. O curso Data Formats cobre as regras das tags que decidem até que ponto isto te afeta.
Assinaturas pós-quânticas com crypto/mldsa
O RSA e o ECDSA são seguros hoje porque nenhum computador consegue fatorizar números grandes ou resolver logaritmos discretos com rapidez suficiente. Um computador quântico suficientemente grande conseguiria. O crypto/mldsa implementa o ML-DSA da FIPS 204 em três conjuntos de parâmetros, 44, 65 e 87, na sequência da proposta #77626.
Outros pacotes já o suportam. O crypto/x509 analisa e verifica chaves e assinaturas ML-DSA, e o crypto/tls aceita os três conjuntos de parâmetros num handshake TLS 1.3. O MLKEM1024 junta-se às trocas de chaves suportadas para um acordo de chaves resistente à computação quântica, e ativa-se ao acrescentá-lo a Config.CurvePreferences.
Uma assinatura e verificação completas para uma versão de firmware:
example.gogosk, err := mldsa.GenerateKey(mldsa.MLDSA65()) if err != nil { return err } firmware, err := os.ReadFile("firmware-v2.1.0.bin") if err != nil { return err } // Context is a domain-separation label. Sign and verify must pass the same one. opts := &mldsa.Options{Context: "acme/firmware-release"} sig, err := sk.Sign(nil, firmware, opts) if err != nil { return err } fmt.Println(mldsa.Verify(sk.PublicKey(), firmware, sig, opts) == nil) // true fmt.Println(mldsa.MLDSA65().SignatureSize()) // 3309 firmware[0] ^= 1 // flip one bit fmt.Println(mldsa.Verify(sk.PublicKey(), firmware, sig, opts) == nil) // false
Verify devolve nil quando a assinatura é válida, por isso comparar com nil dá-te um booleano. O inconveniente é o tamanho, e a diferença é grande:

Fonte: tamanhos das assinaturas segundo a FIPS 204 (ML-DSA) e as especificações dos respetivos algoritmos.
Uma assinatura ML-DSA-65 tem cerca de 46 vezes o tamanho de uma Ed25519, por isso só vale a pena usá-la onde a assinatura tem de durar. Firmware e artefactos de release em que um dispositivo ainda tem de confiar daqui a dez anos justificam os 3 KB. O mesmo vale para raízes de certificados pensadas para sobreviver à criptografia atual e para servidores TLS cujas chaves preferes não ter de trocar quando os computadores quânticos aparecerem. Para tokens de sessão de curta duração, que emites aos milhares por segundo, o tamanho extra é um overhead de que ainda não precisas.
Na mesma área chega uma alteração do crypto/x509 sem relação com isto. O SystemCertPool respeita agora SSL_CERT_FILE e SSL_CERT_DIR no Windows e no Darwin, e não apenas no Linux. Quando estas variáveis estão definidas, o Go carrega as raízes a partir do disco e usa o seu próprio verificador em vez das APIs da plataforma. Define GODEBUG=x509sslcertoverrideplatform=0 para manter o comportamento antigo.
Veredicto: adota para assinaturas de longa duração, ignora para tudo o resto.
O pacote experimental simd
Uma instrução normal do CPU trabalha com um valor. Uma instrução SIMD trabalha com um vetor inteiro de valores num só passo, por isso o mesmo resultado exige muito menos instruções. Isso ajuda quando aplicas as mesmas contas a um slice grande de amostras de áudio ou de píxeis de imagem, ou quando calculas um produto escalar. Código que sobretudo manipula structs e strings não ganha nada.
Cada arquitetura expõe o SIMD de forma diferente. O novo pacote simd é portável e não pressupõe um tamanho de vetor. Está disponível em todas as arquiteturas e usa instruções de hardware onde elas existem. Ativa-o com GOEXPERIMENT=simd no momento do build. É experimental, por isso a API pode mudar.
Misturar duas faixas de áudio, várias amostras por passo:
example.gogo//go:build goexperiment.simd // out[i] = trackA[i] + trackB[i], several samples at a time. out := make([]float32, len(trackA)) lanes := simd.LoadFloat32s(trackA).Len() // how many float32s fit in one vector for i := 0; i+lanes <= len(trackA); i += lanes { va := simd.LoadFloat32s(trackA[i:]) vb := simd.LoadFloat32s(trackB[i:]) va.Add(vb).Store(out[i:]) } // A plain scalar loop handles the leftover tail.
Len indica quantos elementos cabem num vetor na máquina em que estás a correr, por isso o ciclo nunca tem de fixar no código a largura do vetor. Cada passo carrega esse número de amostras das duas faixas, soma-as elemento a elemento numa só operação e guarda o resultado.
Veredicto: ignora por agora, a menos que já faças profiling de um ciclo numérico crítico. A API está atrás de um GOEXPERIMENT e sujeita a alterações.
Desempenho que ganhas só por atualizar
Algumas melhorias de velocidade só pedem uma recompilação. A maior é a alocação especializada por tamanho, que as notas de lançamento descrevem assim:

Fonte: notas de lançamento do Go 1.27, alocação de memória mais rápida.
Estes dois números medem coisas diferentes. Os 30 % são o custo de uma única chamada de alocação pequena. O ~1 % é o que um serviço inteiro vê. Se encontrares uma regressão, GOEXPERIMENT=nosizespecializedmalloc desativa a funcionalidade, e espera-se que essa válvula de escape desapareça no Go 1.28.
Três otimizações do compilador também estão ativas por omissão. Uma passagem de análise de fluxo de dados de bits conhecidos (known bits) regista que bits de um valor são comprovadamente 0 ou 1 e elimina a redundância resultante. A movimentação de código invariante de ciclo (loop-invariant code motion) tira do corpo do ciclo os cálculos cujo resultado nunca muda, para que corram uma vez em vez de em cada iteração. E as instruções switch passam a compilar para tabelas de consulta quando os casos o permitem, incluindo com fallthrough, e saltam diretamente para o caso correspondente em vez de testarem um a um.
O compress/flate também fica mais rápido, com uma ressalva: a saída codificada exata pode ser diferente da do Go 1.26. O DEFLATE está por baixo de archive/zip, compress/gzip, compress/zlib e image/png, por isso os testes golden que comparam byte a byte a saída de qualquer um deles podem falhar. A saída continua correta, por isso regenera as fixtures.
Veredicto: adota já. Recebes isto ao atualizar de qualquer forma.
Encontrar fugas de goroutines e ler tracebacks com etiquetas
O perfil goroutineleak deixou de ser experimental e passou a estar disponível para todos, e o GOEXPERIMENT goroutineleakprofile foi eliminado. Está exposto através de runtime/pprof e como o endpoint /debug/pprof/goroutineleak do net/http/pprof.
Uma goroutine com fuga é uma goroutine bloqueada numa primitiva de concorrência que nunca pode ser desbloqueada. O runtime encontra-as através do garbage collector: se a goroutine G está bloqueada na primitiva P, e P não é alcançável a partir de nenhuma goroutine executável nem de nada que essas goroutines possam desbloquear, então G nunca pode acordar.
example.gogofunc startJob() { result := make(chan int) // unbuffered: the send waits for a receiver go func() { result <- expensiveWork() // blocks forever, nobody receives }() // returns without receiving, so result becomes unreachable } func main() { startJob() runtime.GC() // the detector scans during a GC cycle pprof.Lookup("goroutineleak").WriteTo(os.Stdout, 1) // goroutineleak profile: total 1 // ... main.startJob.func1 ... main.go:14 }
Esta abordagem tem um ponto cego. O runtime pode não detetar fugas em que a primitiva bloqueante é alcançável através de uma variável global ou das variáveis locais de uma goroutine executável. Um channel guardado num registo ao nível do pacote conta como alcançável, por isso o perfil não o reporta. O curso Concurrency Fundamentals percorre os padrões de fuga que produzem estes casos e mostra como os fechar com context.
A segunda melhoria de depuração não exige código nenhum. Nos módulos cuja diretiva go é 1.27 ou posterior, os tracebacks passam a incluir as etiquetas (labels) de goroutine do runtime/pprof na linha de cabeçalho. As etiquetas que já defines para profiling aparecem em crash dumps, traces de SIGQUIT e na saída de runtime.Stack. Quando duas goroutines têm stacks idênticas, a etiqueta é muitas vezes a única forma de as distinguir:
example.gogopprof.Do(ctx, pprof.Labels("request", id), func(ctx context.Context) { work() }) // goroutine 34 [running]: // labels: {"request":"req-42"} // main.handle.func1 ...
Define GODEBUG=tracebacklabels=0 para desativar isto se as tuas etiquetas contêm alguma coisa que não queres num crash dump.
Veredicto: adota já. Nenhum dos dois te custa nada.
Pequenas melhorias na linguagem e na biblioteca
Seletores de campo em literais de struct
Uma chave num literal de struct pode agora ser qualquer seletor de campo válido para o tipo, e não apenas o nome de um campo de nível superior (proposta #9859). Se incorporas um modelo partilhado, já não tens de escrever o literal aninhado:
example.gogotype Model struct { ID int64 CreatedAt time.Time } type Post struct { Model Author string Likes int } p := Post{Model: Model{ID: 42}, Author: "Patrik"} // before p := Post{ID: 42, Author: "Patrik"} // Go 1.27
Inferência generalizada de tipos de função
A inferência de tipos de função aplica-se agora em todos os contextos em que uma função genérica é atribuída ou convertida a um tipo de função compatível (proposta #77245). Antes, só funcionava numa declaração de variável com tipo:
example.gogofunc ascending[T cmp.Ordered](a, b T) int { return cmp.Compare(a, b) } func descending[T cmp.Ordered](a, b T) int { return cmp.Compare(b, a) } orders := []func(int, int) int{ascending[int], descending[int]} // before orders := []func(int, int) int{ascending, descending} // Go 1.27
O tipo dos elementos do slice é func(int, int) int, por isso o Go infere que T é int. O mesmo funciona agora para conversões e para passar como argumento uma função genérica sem instanciar.
strings.CutLast e bytes.CutLast
Cut divide no primeiro separador. CutLast divide no último e devolve a parte anterior, a parte seguinte e se o separador foi encontrado (proposta #71151):
example.gogodir, file, found := strings.CutLast("internal/store/user.go", "/") fmt.Println(dir, file, found) // internal/store user.go true
Antes, chamavas LastIndex, verificavas se era -1 e fazias slicing duas vezes à mão.
Rand.N, maphash.Hasher e big.Int.Divide
O math/rand/v2 ganha Rand.N como método genérico (proposta #77853), por isso podes tirar um valor aleatório limitado de uma fonte com a seed que escolheste, em vez da fonte global, que não aceita seed. Um teste que falha repete-se então com as mesmas entradas.
O hash/maphash ganha uma interface genérica Hasher[T] para estruturas baseadas em hash, como maps personalizados e filtros de Bloom (proposta #70471). Junta uma função de hash a uma verificação de igualdade, e valores iguais têm de produzir o mesmo hash. ComparableHasher[T] é a implementação pronta a usar para tipos comparáveis e compara com ==. O go/types já a usa e inclui um Hasher que respeita a relação Identical.
O math/big.Int ganha um método Divide que calcula o quociente e o resto em conjunto, com o modo de arredondamento que escolheres: Trunc, Floor, Round ou Ceil. Quo e Mod truncam sempre em direção a zero, por isso o código financeiro e numérico que precisa de outro arredondamento passa a ter uma opção incluída.
Unicode 17, database/sql e o resto
O pacote unicode e tudo o que assenta nele passou diretamente do Unicode 15 para o Unicode 17, por isso os caracteres acrescentados nas duas versões intermédias passam a ser classificados corretamente. O database/sql ganhou ConvertAssign, que permite aos drivers reutilizar as conversões de tipo que Rows.Scan faz em vez de escreverem as suas.
O resto são alterações pequenas:
cryptoacrescenta o valor de hashMLDSAMu, um mecanismo de sinalização para assinaturas ML-DSA com external-mu.crypto/ecdsaverifica agora se o comprimento do hash está correto emPrivateKey.Signquando passasSignerOptsnão nil.crypto/x509expõeRawSignatureAlgorithmemCertificate,CertificateRequesteRevocationList, o que te dá o AlgorithmIdentifier codificado em DER mesmo quandoSignatureAlgorithméUnknownSignatureAlgorithm. A análise parapkix.Nametambém aceita uma gama mais ampla de tipos de valor, e os desconhecidos vão parar aasn1.RawValue.crypto/tlsacrescentaConnectionState.LocalCertificate, a cadeia que apresentaste ao peer, e aindaQUICConfig.ClientHelloInfoConn.Config.Randfica obsoleto em favor detesting/cryptotest.SetGlobalRandompara testes deterministas.netfaz com que os métodos de leitura deUnixConndevolvamio.EOFdiretamente, em vez de o envolverem numnet.OpError.runtime/secretpropaga o modo secreto às goroutines criadas dentro dele.go/constantacrescentaStringLen,go/scanneracrescentaScanner.Endego/tokendá aFileum métodoString.- Quanto a plataformas, a versão big-endian de 64 bits para PowerPC em Linux passa para a ABI de sistema ELFv2, o que desbloqueia cgo, executáveis independentes da posição e linking externo nessa plataforma, e exige um kernel Linux 3.13 ou posterior. No Plan 9,
syscall.Errnopassa a estar definido e implementaerror, por isso o código portável que o refere compila sem build constraints. O linker também aceita-macose-macsdkpara definir as versões escritas no load commandLC_BUILD_VERSIONdo macOS.
O que mudou na toolchain do Go?
O go fix ganhou quatro modernizadores. embedlit simplifica referências a campos incorporados em literais compostos, algo que a nova regra dos literais de struct torna possível. atomictypes substitui tipos básicos em chamadas a sync/atomic por tipos atómicos, slicesbackward reescreve ciclos para trás com slices.Backward e unsafefuncs substitui aritmética de ponteiros unsafe por chamadas a funções. Vieram também duas alterações de manutenção: fmtappendf foi removido por questões de estilo e waitgroup passou a chamar-se waitgroupgo. Corre-o uma vez depois de atualizares a diretiva go:
example.bashbashgo fix -diff ./... # preview go fix ./... # apply
Para o catálogo completo do que o go fix reescreve, vê todos os modernizadores do go fix no Go 1.26.
O go test executa agora por omissão a verificação stdversion do vet. Esta assinala símbolos da biblioteca padrão mais recentes do que a versão do Go em vigor para o ficheiro, definida pela diretiva do teu go.mod e pelas build tags. Se o teu módulo declara go 1.25 e alguém usa strings.CutLast, a execução dos testes apanha-o antes de um utilizador no 1.25. O go test -json também anota as linhas "Action":"output" com um campo opcional "OutputType", atualmente error, error-continue ou frame, o que é útil se fazes parsing da saída dos testes em CI.
O go doc aceita agora a sintaxe package@version, por isso go doc rsc.io/[email protected] mostra a documentação de uma versão exata sem teres de fazer checkout dela. Uma nova flag -ex lista os exemplos executáveis de um pacote, e indicar um deles mostra o respetivo código-fonte.
O go mod tidy impõe uma organização em dois blocos nos módulos com go 1.27 ou posterior e junta os blocos require dispersos num bloco direto e num bloco indireto. Os blocos de comentários associados a dependências são preservados, e um comentário que abrange um conjunto misto passa para o bloco direto.
O go tool trace -http=:6060 passa a escutar apenas em localhost, tal como o go tool pprof, por isso indica um endereço completo como -http=0.0.0.0:6060 se precisares de acesso a partir de outra máquina. As ferramentas compile, link, asm, cgo, cover e pack também aceitam ficheiros de resposta (@file) num formato compatível com o GCC, o que ajuda os sistemas de build que excedem os limites de comprimento da linha de comandos.
Testes e net/http
O httptest.NewTestServer cria um Server numa rede falsa em memória, pensado para usar com testing/synctest, por isso não há nenhuma porta TCP real envolvida. O seu parceiro é o synctest.Sleep, que faz time.Sleep e synctest.Wait numa só chamada: avança o relógio falso e depois deixa as goroutines estabilizar. Com os dois, os testes HTTP deixam de depender de tempos. O curso Professional Go Testing cobre o modelo do synctest em que estes assentam.
example.gogosrv := httptest.NewTestServer(t, handler) // in-memory, cleanup auto-registered
Do lado do net/http, o servidor HTTP/2 aceita agora os sinais de prioridade do cliente definidos no RFC 9218 e serve primeiro os streams de maior prioridade. Define Server.DisableClientPriority = true para voltar ao comportamento round-robin antigo.
A alteração que tens mais probabilidade de notar está no HTTP/1: fechar um Response.Body lido apenas em parte passa a consumir o conteúdo restante até um limite conservador, para que a ligação possa ser reutilizada. Para a maioria dos programas isto não muda nada ou dá um pequeno ganho de velocidade. Se fechas cedo para abortar um download grande, Transport.DisableKeepAlives = true desativa este comportamento.
Server.MaxHeaderValueCount limita quantos valores um único cabeçalho pode ter, o que protege contra pedidos que inundam um cabeçalho. Transport e Server conseguem negociar TLS ALPN numa net.Conn que forneças tu próprio, desde que implemente ConnectionState() tls.ConnectionState, por isso o HTTP/2 passa a ser usado em ligações por túnel ou através de proxy. E o net/url ganhou URL.Clone e Values.Clone para cópias profundas. O curso HTTP and Networking cobre as definições do servidor e do transport em que estas novidades assentam.
Veredicto: adota já. Se tens uma suite de testes HTTP instável, o httptest com o synctest é razão suficiente para atualizar.
O que deixa de funcionar quando atualizas para o Go 1.27?
As notas de lançamento distribuem as alterações incompatíveis por oito secções. Esta tabela reúne-as, ordenadas pela probabilidade de te afetarem.
| O que muda | A quem afeta | O que fazer |
|---|---|---|
O compress/flate produz bytes diferentes | Testes golden que comparam byte a byte a saída de gzip, zlib, zip ou png | Regenera as fixtures. A compressão está correta, só é diferente. |
| Nomes mais simples para funções literais (closures) | Testes que verificam nomes de símbolos e código que compara ponteiros de código de funções | Deixa de depender dos nomes das funções literais. A comparação de ponteiros de funções já estava documentada como pouco fiável. |
GODEBUGs asynctimerchan e gotypesalias removidos definitivamente | Tudo o que os fixa no valor antigo no go.mod ou numa linha //go:debug | grep -r asynctimerchan . antes de atualizar. Definir o valor final por omissão continua a compilar, definir o valor antigo falha. |
Cinco GODEBUGs de TLS/x509 removidos: tlsunsafeekm, tlsrsakex, tls3des, tls10server, x509keypairleaf | Serviços ainda presos a comportamento TLS legado | A mesma regra de cima: o comando go aceita o valor final por omissão e rejeita o valor antigo, por isso encontras estes casos no build. |
| O macOS 13 Ventura é a versão mínima | Runners de CI e máquinas de desenvolvimento com macOS mais antigo | Atualiza a imagem do runner. Foi anunciado nas notas do Go 1.26. |
Suporte para bzr removido do comando go | Módulos alojados em servidores Bazaar | Faz mirror da dependência ou faz vendoring dela. |
A nova diretiva //go:linknamestd marca linknames exclusivos da std, o linker passa a verificar o acesso por linkname a símbolos assembly e os descritores de tipo passaram para uma secção .go.type | Pacotes que acedem ao runtime através de um //go:linkname não autorizado, teus ou de uma dependência | Atualiza a dependência. Isto falha no build, e de forma bem visível. Nada disto está nas notas de lançamento, por isso é o build que te avisa. |
O Response.Body.Close do HTTP/1 consome o conteúdo por ler | Código que fecha cedo para abortar um download grande | Define Transport.DisableKeepAlives = true nesses clientes. |
| O texto das mensagens de erro do json/v2 é diferente | Testes que verificam strings exatas de erros JSON | Compara pelo tipo de erro ou por uma substring em vez da mensagem completa. |
A última linha é a mais difícil de detetar. O comportamento mantém-se mas o texto não, por isso uma suite de testes que compara strings de erros de unmarshal falha sem causa evidente. Isola o problema com duas execuções num branch que já tenha atualizado o go.mod para go 1.27:
example.bashbashgo test ./... > default.txt 2>&1 GOEXPERIMENT=nojsonv2 go test ./... > nojsonv2.txt 2>&1 diff default.txt nojsonv2.txt
Tudo o que aparecer no diff vem da alteração ao JSON. Tudo o que falhar nas duas execuções é outra coisa da tabela. Espera-se que GOEXPERIMENT=nojsonv2 seja removido numa versão futura, por isso usa-o para diagnosticar e depois corrige os testes.
Perguntas frequentes
Tenho de reescrever o meu código JSON para o Go 1.27?
Não. O pacote encoding/json passa a estar implementado sobre a v2, mas o comportamento de marshaling e unmarshaling mantém-se e a API v1 continua suportada. A única diferença observável é que o texto exato das mensagens de erro pode mudar. Só adotas as predefinições mais rigorosas da v2 se importares encoding/json/v2 explicitamente.
Um método genérico pode satisfazer uma interface?
Não. As notas de lançamento indicam que os métodos de interface não podem declarar parâmetros de tipo e que os métodos de interface não podem ser implementados por métodos genéricos. O despacho de interfaces é resolvido em runtime, por isso o compilador não consegue saber que instanciações gerar para uma chamada despachada dinamicamente. Se a tua API assenta em interfaces, continua a usar funções genéricas ao nível do pacote.
Devo trocar o github.com/google/uuid pelo pacote da biblioteca padrão?
Em código novo, sim. Em código existente, verifica primeiro duas coisas. O UUID da biblioteca padrão é um [16]byte com o seu próprio conjunto de métodos, por isso qualquer código que use os métodos extra do tipo de terceiros precisa de revisão. Além disso, qualquer dependência que exponha o uuid.UUID de terceiros na sua API mantém esse módulo no teu grafo de dependências de qualquer forma.
O crypto/mldsa está pronto para produção?
É um pacote estável da biblioteca padrão que implementa a FIPS 204 e está integrado no crypto/x509 e no crypto/tls. A limitação prática é o tamanho da assinatura, não a maturidade: as assinaturas ML-DSA-65 têm 3.309 bytes, contra 64 no Ed25519. Usa-o onde a assinatura tiver de sobreviver à criptografia atual e dispensa-o para tokens de curta duração emitidos em grande volume.
Qual é a forma mais rápida de descobrir o que deixa de funcionar no meu código com o Go 1.27?
Atualiza o go.mod para go 1.27 num branch e depois faz grep de asynctimerchan, gotypesalias e das cinco definições de TLS removidas, porque essas fazem falhar o build logo à partida. A seguir corre a tua suite duas vezes, uma normalmente e outra com GOEXPERIMENT=nojsonv2, e compara as falhas com diff. Assim separas as diferenças no texto dos erros JSON das falhas em ficheiros golden e em nomes de símbolos.
Fontes
Referências primárias citadas neste artigo (última verificação a 23 de agosto de 2026):
- Notas de lançamento do Go 1.27, consultado a 23/08/2026
- Tour interativo do Go 1.27, VictoriaMetrics, consultado a 23/08/2026
- Documentação do pacote
uuid, consultado a 23/08/2026 - Documentação do pacote
encoding/json/v2, consultado a 23/08/2026 encoding/json: Migrating to v2, a lista completa das diferenças de comportamento, consultado a 23/08/2026- Documentação do pacote
crypto/mldsa, consultado a 23/08/2026 - Documentação do pacote
simd, consultado a 23/08/2026 - Documentação do pacote
net/http/httptest, consultado a 23/08/2026 - NIST FIPS 204: Module-Lattice-Based Digital Signature Standard, consultado a 23/08/2026
- Documentação do analisador modernize do
x/tools, consultado a 23/08/2026 - Notas de lançamento do Unicode 17.0.0, consultado a 23/08/2026
Continua a aprender
Os métodos genéricos são mais fáceis de usar bem quando as restrições e a inferência já te parecem naturais, e são essas as partes que muita gente lê por alto quando aprende genéricos pela primeira vez. O curso Go Generics Masterclass trabalha restrições, conjuntos de tipos e inferência com exercícios no navegador que correm na toolchain atual, por isso podes experimentar a sintaxe dos métodos do 1.27 em código real.
Estás a começar com Go? Começa pelo percurso Go Fundamentals e volta depois para as notas de lançamento. E se saltaste a versão do ano passado, Novidades do Go 1.26 cobre errors.AsType, o garbage collector Green Tea e new(expr).
