---
id: 1a31e06c0d969b4b6cbbc590259cf089
author: iwanhae
title: Go 1.27, ou Como Aprendi a Parar de Me Preocupar e Passar a Amar os Métodos Genéricos
description: Métodos genéricos, encoding/json/v2, um pacote uuid padrão e um detector de vazamento de goroutines que de fato funciona. Um tour pelo Go 1.27 com exemplos que foram todos efetivamente executados.
language: pt
date: 2026-08-15T02:08:43.129534158Z
path: /blog/posts/go-1-27-or-how-i-learned-to-stop-worrying-and-love-generic-methods-za800e454
category: release
---

![Visão geral do Go 1.27](/assets/images/whats-new-in-go1.27/overview.webp)

A versão Go 1.27 chega em agosto de 2026, e desta vez a linguagem em si mudou. Não é a mudança do tipo "adicionamos uma nova função a `slices`", mas uma mudança real. Métodos genéricos foram incorporados. Uma issue com onze anos foi fechada. O `encoding/json` foi substituído silenciosamente por um novo motor enquanto o avião ainda estava no ar.

Todos os exemplos abaixo foram executados com `go1.27rc3` (darwin/arm64). Todos os blocos de saída são reais, copiados diretamente, incluindo as mensagens de erro. Se algo parecer estranho, é porque foi exatamente isso que o compilador disse.

```bash
go install golang.org/dl/go1.27rc3@latest
go1.27rc3 download
```

## 1. A linguagem mudou (sim, de verdade)

### 1.1. Métodos genéricos

Desde o Go 1.18 temos genéricos e, desde o Go 1.18, temos também A Conversa. Ela segue mais ou menos assim:

> "Vou apenas escrever um método `Map` no meu tipo slice..."
>
> `method must have no type parameters`
>
> "...tudo bem. Então será uma função de nível de pacote."

Os métodos só podiam usar parâmetros de tipo declarados pelo _receptor_. Um método próprio não podia introduzir novos parâmetros. Assim, toda transformação genérica era exilada para o escopo do pacote, onde ficava ao lado de outras dezessete funções auxiliares soltas com algum nome derivado de `MapSlice`.

O Go 1.27 corrige isso:

```go
type List[E any] []E

// F é um parâmetro de tipo declarado pelo próprio método
func (l List[E]) Apply[F any](f func(E) F) List[F] {
	r := make(List[F], len(l))
	for i, x := range l {
		r[i] = f(x)
	}
	return r
}

func main() {
	l := List[int]{1, 2, 3}
	fmt.Println(l.Apply(func(i int) string { return fmt.Sprint(i * 10) }))
	// [10 20 30]
}
```

O receptor sequer precisa ser genérico. Uma struct comum pode ter um método genérico:

```go
type Bag struct{ items []any }

func (b *Bag) Add[T any](v T) { b.items = append(b.items, v) }
```

Agora, antes de refatorar toda a sua base de código nesta tarde, há **duas restrições**, e elas importam mais do que parecem:

1. Métodos de interfaces não podem declarar parâmetros de tipo.
2. Métodos genéricos não podem implementar métodos de interfaces.

Esta segunda é a que vai te pegar:

```go
type Adder interface{ Add(int) int }

type G struct{}

func (G) Add[T any](v T) T { return v }

var _ Adder = G{}
```

```
cannot use G{} (value of struct type G) as Adder value in variable declaration:
	G does not implement Adder (wrong type for method Add)
		have Add[T any](T) T
		want Add(int) int
```

Ou seja, métodos genéricos e despacho dinâmico não se misturam. Isso não é mesquinhez da equipe do Go: um método genérico tem infinitas instanciações, e a tabela de métodos de uma interface precisa ser finita e conhecida em tempo de compilação. Não se pode colocar algo infinito em uma tabela finita. O universo disse não.

Leitura prática: métodos genéricos são para tipos concretos com APIs de utilitários. Qualquer coisa que precise se esconder atrás de uma interface ainda precisa da abordagem antiga.

A biblioteca padrão já tirou proveito disso. O `Rand` de `math/rand/v2` ganhou um método genérico:

```go
func (r *Rand) N[Int intType](n Int) Int
```

```go
r := rand.New(rand.NewPCG(1, 2))
fmt.Println(r.N(int32(100)))     // 76
fmt.Println(r.N(time.Second))    // 616.436223ms
```

Antes, apenas a função `rand.N` de nível de pacote era genérica, o que significava usar a fonte global. Agora, o seu próprio `*Rand` com seed recebe a mesma comodidade. Pequena mudança. Mudança boa.

### 1.2. Seletores de campos em literais de struct, ou: a Issue #9859 finalmente é resolvida

A embedding permite escrever `u.ID`. A embedding _não_ permite escrever `User{ID: 1}`. Em vez disso, é preciso escrever `User{Base: Base{ID: 1}}`, que é a maneira que o Go tem de perguntar se você realmente queria isso.

A [Issue #9859](https://go.dev/issue/9859) foi aberta em 2015. Agora foi fechada. Em algum lugar, um gopher que desde então mudou de carreira duas vezes está recebendo uma notificação do GitHub.

```go
type Object struct{ name, color string }
type Point3D struct {
	Object
	x, y, z float64
}
type Line struct {
	Object
	p, q Point3D
}

// Go 1.27: use o campo promovido diretamente
line := Line{name: "diagonal", q: Point3D{y: -4, z: 12.3}}
```

`name` não é um campo de `Line`. É um campo do `Object` embutido. Antes, seria preciso escrever `Line{Object: Object{name: "diagonal"}, ...}`.

Agora, os detalhes, que verifiquei tentando compilar todo o código.

**(a) A chave continua sendo um identificador simples.** Não é possível escrever um caminho de seletor arbitrário.

```go
_ = Line{Object.name: "x"}   // invalid field name Object.name in struct literal
_ = Line{p.x: 1}             // invalid field name p.x in struct literal
```

Portanto, o recurso não é "as chaves agora funcionam como acesso a campos". Ele é especificamente "nomes de campos implicitamente promovidos são permitidos". Um pouco mais restrito do que a manchete sugere.

**(b) Não é possível especificar um campo embutido e, ao mesmo tempo, algo promovido a partir dele.**

```go
obj := Object{"edge", "black"}
_ = Line{Object: obj, name: "diagonal"}
// cannot specify promoted field name and enclosing embedded field Object
```

Razoável. Você deu duas informações conflitantes sobre a mesma memória, e o compilador se recusou a adivinhar.

**(c) Embedding de ponteiro não é bem-vindo.**

```go
type PtrEmbed struct {
	*Object
	z int
}

_ = PtrEmbed{name: "x"}
// invalid implicit pointer indirection to reach name
```

Para seguir um ponteiro, é preciso ter um ponteiro a seguir, e no momento da construção do literal ele ainda não existe. Também faz sentido.

Boa notícia: o `go fix` reescreve seus literais antigos automaticamente. Mais sobre isso adiante.

### 1.3. A inferência de tipos em funções ficou menos arbitrária

A inferência de tipos para funções genéricas funcionava em alguns lugares e em outros não, sem um princípio claro que explicasse a diferença. Atribuir a uma variável? Funcionava. Colocar exatamente a mesma função em um campo de struct? Erro de compilação, por favor escreva `double[int]` como em 2022.

O Go 1.27 faz a inferência funcionar em **todo contexto em que o tipo alvo é conhecido sem ambiguidade**:

```go
func double[T ~int | ~float64](v T) T { return v * 2 }

type S struct{ f func(int) int }
type A [1]func(int) int

func main() {
	s := S{f: double}          // 1.26: precisava de double[int]
	a := A{double}             // 1.26: precisava de double[int]

	c := make(chan func(int) int, 1)
	c <- double                // 1.26: precisava de double[int]

	var fn func(float64) float64 = double  // isto sempre funcionou

	fmt.Println(s.f(21), a[0](5), (<-c)(7), fn(1.5))
	// 42 10 14 3
}
```

Uma única `double`, instanciada como `func(int) int` em três lugares e como `func(float64) float64` em um quarto, inteiramente a partir do contexto. Se você escreve structs de opções ou tabelas de handlers cheias de valores de função, uma boa quantidade de ruído `[T]` está prestes a desaparecer da sua base de código.

## 2. Runtime: desempenho gratuito e um detector de vazamentos

### 2.1. Alocação especializada por tamanho

O compilador agora emite chamadas a rotinas de alocação especializadas por tamanho para objetos pequenos. As notas de lançamento afirmam até 30% de redução nas alocações abaixo de 80 bytes, e cerca de 1% no geral para programas com muitas alocações.

Não acredito em notas de lançamento sem medir, então:

```go
type small struct{ a, b, c, d int }

var sink any

func BenchmarkAlloc(b *testing.B) {
	for b.Loop() {
		sink = &small{}
	}
}

func BenchmarkSlice(b *testing.B) {
	for b.Loop() {
		sink = make([]byte, 48)
	}
}
```

```
# padrão (malloc especializado por tamanho ativado)
BenchmarkAlloc-12     3000000     5.358 ns/op
BenchmarkSlice-12     3000000    13.58 ns/op

# GOEXPERIMENT=nosizespecializedmalloc
BenchmarkAlloc-12     3000000     8.722 ns/op
BenchmarkSlice-12     3000000    20.81 ns/op

```

30–35% mais rápido em um microbenchmark que só faz alocações em Apple Silicon. Ou seja: este é o melhor cenário, obtido em condições de laboratório por um benchmark projetado para deixar o número bonito. Em um programa real, o GC e o trabalho efetivo vão encobrir a maior parte disso. A afirmação de cerca de 1% é a honesta.

O custo está no tamanho do binário. Hello World:

```
2413202 bytes  (padrão)
2361826 bytes  (nosizespecializedmalloc)
```

Cerca de 50KB, fixos, independentemente do seu programa. É possível desativar com `GOEXPERIMENT=nosizespecializedmalloc`, mas **essa saída de emergência está programada para remoção no Go 1.28**, então trate-a como um paliativo para relatórios de bugs, e não como um estilo de vida.

### 2.2. O perfil de vazamento de goroutines agora é real

Experimental no Go 1.26, disponível em geral na 1.27. O GOEXPERIMENT `goroutineleakprofile` deixou de existir; ele simplesmente funciona.

```go
func leak() {
	ch := make(chan int) // ninguém vai enviar nada. ninguém.
	go func() {
		<-ch
	}()
}

func main() {
	for range 3 {
		leak()
	}
	runtime.GC()
	runtime.GC()
	pprof.Lookup("goroutineleak").WriteTo(os.Stdout, 1)
}
```

```
goroutineleak profile: total 3
3 @ 0x104de3f18 0x104d7f0b0 0x104d7ec34 0x104e374f4 0x104dea024
#	0x104e374f3	main.leak.func1+0x23	/tmp/go127/leak/main.go:12
```

Três goroutines, permanentemente travadas, com um arquivo e uma linha apontando exatamente onde você as criou.

O truque por trás disso é genuinamente engenhoso: ele **reaproveita a análise de alcançabilidade do coletor de lixo**. Se a goroutine G está bloqueada no primitivo P, e P não é alcançável a partir de nenhuma goroutine executável (nem de nada que essas goroutines possam acordar), então nada pode tocar em P novamente, e G nunca vai acordar. Isso não é uma heurística, é uma prova.

O mesmo design traz de graça a limitação: se o canal ou o mutex é alcançável por meio de uma variável global, ou por meio de uma variável local de alguma goroutine que ainda está em execução, o GC ainda pode enxergá-lo, e o runtime não consegue concluir nada. Observe que até o exemplo simples acima precisa de duas chamadas a `runtime.GC()` para gerar o relatório. Ele não pega tudo.

No entanto, ele vai pegar o clássico "esqueci de cancelar o contexto, e a goroutine do worker vive para sempre", que é a maioria dos vazamentos na maior parte do tempo.

Se você importar `net/http/pprof`, o perfil também fica disponível em `/debug/pprof/goroutineleak`. Conecte isso ao ambiente de staging, verifique semanalmente e fique discretamente horrorizado.

Este trabalho foi contribuído por Vlad Saioc, da Uber, que merece uma bebida de sua escolha.

### 2.3. Tracebacks agora informam qual requisição falhou

Para módulos que declaram Go 1.27 ou posterior, as linhas de cabeçalho dos tracebacks agora incluem os rótulos de goroutine do `runtime/pprof`.

```go
func handle(ctx context.Context) {
	var wg sync.WaitGroup
	wg.Go(func() {
		buf := make([]byte, 4<<10)
		os.Stdout.Write(buf[:runtime.Stack(buf, true)])
	})
	wg.Wait()
}

func main() {
	labels := pprof.Labels("request", "GET /orders/42", "tenant", "acme")
	pprof.Do(context.Background(), labels, handle)
}
```

```
goroutine 3 [running] {request: "GET /orders/42", tenant: acme}:
main.handle.func1()
	/tmp/go127/tblabel/main.go:15 +0x40
sync.(*WaitGroup).Go.func1()
	...
goroutine 1 [sync.WaitGroup.Wait] {request: "GET /orders/42", tenant: acme}:
...
```

O detalhe importante: **os rótulos são herdados pelas goroutines filhas.** A goroutine 3 nunca definiu um rótulo. Ela recebeu um do pai.

Assim, da próxima vez que a produção travar e alguém enviar SIGQUIT para o processo, em vez de 4.000 goroutines que parecem todas idênticas, você verá 4.000 goroutines marcadas com a requisição e o tenant a que pertencem. Três linhas de `pprof.Do` no ponto de entrada da sua requisição garantem isso.

Como os rótulos podem conter coisas que você preferiria não despejar no stderr, `GODEBUG=tracebacklabels=0` desativa o recurso, e essa opção de desativação foi explicitamente planejada para permanecer para sempre.

### 2.4. asynctimerchan foi removido definitivamente

O Go 1.23 tornou os canais de timer não bufferizados (síncronos) e ofereceu `asynctimerchan=1` como forma de voltar ao comportamento antigo. Na 1.27, essa configuração é **removida permanentemente**. Os canais do pacote `time` são síncronos, ponto final, sem negociação.

A parte interessante é a política que acompanhou essa mudança. Um GODEBUG removido que permanece no seu `go.mod` não quebra o build automaticamente; ele só quebra se estiver definido com o valor **antigo**:

```
# go.mod contém: godebug asynctimerchan=1
go: error loading go.mod:
go.mod:5: removed GODEBUG "asynctimerchan" set to old value "1" (https://go.dev/doc/godebug#go-127)

# go.mod contém: godebug asynctimerchan=0
(compila sem problemas)
```

Isso significa que as únicas pessoas que levam bronca são aquelas que realmente dependiam do comportamento removido. Quem definiu o valor que virou o padrão e esqueceu do assunto pode continuar sem pensar nisso. É uma peça atenciosa de arqueologia de API.

## 3. Biblioteca padrão

### 3.1. encoding/json/v2: trocaram o motor em pleno voo

Este é o grande destaque. [Mais de dois anos de discussão de proposta](https://go.dev/issue/71497) finalmente foram incorporados.

Agora existem **três pacotes**, e entender a divisão é a maior parte do trabalho:

| Pacote                   | Função                                                                         |
| ------------------------ | ------------------------------------------------------------------------------ |
| `encoding/json`          | A API v1 que você conhece. **Comportamento 100% inalterado.** Agora implementada sobre a v2 |
| `encoding/json/v2`       | Processamento semântico. Valores Go ↔ JSON                                     |
| `encoding/json/jsontext` | Processamento sintático. JSON como um fluxo de tokens                          |

A principal novidade é que o `encoding/json` foi reconstruído sobre uma implementação completamente diferente e **se comporta de forma idêntica**. Veja como:

```go
// encoding/json.Unmarshal do Go 1.27, resumido
func Unmarshal(data []byte, v any) error {
	return jsonv2.Unmarshal(data, v, DefaultOptionsV1())
}
```

Cada peculiaridade legada da v1 foi codificada como uma opção, agrupada em `DefaultOptionsV1()`, e a API v1 sempre aplica esse conjunto. Isso significa que o código abaixo se comporta da mesma forma nas versões 1.26 e 1.27:

```go
var m map[string]int
json.Unmarshal([]byte(`{"a":1,"a":2}`), &m)  // <nil>, map[a:2]
```

Sim, a v1 ainda aceita silenciosamente chaves duplicadas e fica com a última. Sempre foi assim. Continua assim. Compatibilidade significa compatibilidade também com as partes ruins.

**A v2, por outro lado, tem opiniões:**

```go
var m map[string]int
jsonv2.Unmarshal([]byte(`{"a":1,"a":2}`), &m)
// jsontext: duplicate object member name "a"

var s string
jsonv2.Unmarshal([]byte("\"\xff\""), &s)
// jsontext: invalid UTF-8 after offset 1
```

Chaves duplicadas e UTF-8 inválido são rejeitados. Ambos são genuinamente perigosos, não apenas desorganizados: quando dois parsers discordam sobre qual chave duplicada prevalece, surgem falhas de segurança. A CVE-2017-12635 do CouchDB foi exatamente isso: um corpo JSON com duas chaves `roles`, em que o validador lia uma e a camada de armazenamento lia a outra. Rejeitar é a decisão correta.

As opções são variádicas:

```go
type Config struct {
	Name    string   `json:"name"`
	Tags    []string `json:"tags,omitzero"`
	Timeout int      `json:"timeout,omitzero"`
}

out, _ := jsonv2.Marshal(Config{Name: "api"})
// {"name":"api"}

// chaves de mapa ordenadas + indentação
out, _ = jsonv2.Marshal(map[string]int{"b": 2, "a": 1},
	jsonv2.Deterministic(true), jsontext.WithIndent("  "))
// {
//   "a": 1,
//   "b": 2
// }

// rejeita campos desconhecidos (antes exigia um Decoder e DisallowUnknownFields())
var c Config
err := jsonv2.Unmarshal([]byte(`{"name":"api","nope":1}`), &c,
	jsonv2.RejectUnknownMembers(true))
// json: cannot unmarshal JSON string into Go main.Config:
//   unknown object member name "nope"
```

`Deterministic`, `MatchCaseInsensitiveNames`, `StringifyNumbers`, `FormatNilSliceAsNull`, `OmitZeroStructFields`: a maioria das coisas que antes você resolvia com uma biblioteca de terceiros ou com um `MarshalJSON` escrito à mão agora são flags.

E a história da migração é a melhor parte. **As opções posteriores prevalecem**, então você pode adotar a rigidez da v2 um comportamento por vez:

```go
// mantém a semântica da v1, mas rejeita chaves duplicadas como a v2
jsonv2.Unmarshal(data, &v,
	json.DefaultOptionsV1(),
	jsontext.AllowDuplicateNames(false))
// duplicate object member name
```

Você não precisa portar uma base de código de 200 mil linhas para a v2 apenas para deixar de aceitar chaves duplicadas. Basta alterar uma opção. Para qualquer projeto grande, esse é o caminho realista.

Desempenho: a serialização fica aproximadamente no mesmo nível, e **a desserialização é significativamente mais rápida.** Se algo der errado, `GOEXPERIMENT=nojsonv2` restaura a implementação antiga. Essa opção de desativação também está programada para ser removida um dia, então abra uma issue em vez de se acomodar.

`jsontext` é a camada de baixo nível: `Encoder`/`Decoder` percorrem JSON como `Token`s e `Value`s, com uma máquina de estados que mantém você honesto. Use-o quando estiver escrevendo um transformador de streaming ou um filtro de JSON e não quiser materializar valores Go de forma alguma.

### 3.2. Um pacote uuid padrão

Finalmente. RFC 9562, na biblioteca padrão, sem necessidade de `go get`.

```go
import "uuid"

func main() {
	fmt.Println(uuid.NewV4())
	// b97aa695-da08-472c-af81-ff088129019f

	// v7: os 48 bits mais significativos são um timestamp, então estes sempre ordenam na ordem de criação
	a, b := uuid.NewV7(), uuid.NewV7()
	fmt.Println(a.Compare(b) < 0) // true

	u, err := uuid.Parse("urn:uuid:0198a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b")
	fmt.Println(u, err)
	// 0198a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b <nil>

	fmt.Println(uuid.Nil(), uuid.Max())
	// 00000000-0000-0000-0000-000000000000 ffffffff-ffff-ffff-ffff-ffffffffffff
}
```

A API inteira é `New`, `NewV4`, `NewV7`, `Nil`, `Max`, `Parse`, `MustParse` e um tipo: `type UUID [16]byte`. Só isso. Você pode ler a documentação completa do pacote enquanto o seu café esfria.

Detalhes que vale conhecer:

- **`UUID` é `[16]byte`**, então `==` funciona e ele pode ser usado diretamente como chave de mapa. Mesmo design do `google/uuid`.
- **`Nil` e `Max` são funções, não variáveis.** Porque uma variável de pacote `var Nil UUID` é uma arma carregada apontada para o seu pé, e alguém, em algum lugar, acabaria atribuindo a ela.
- Os bits aleatórios vêm de um gerador **criptograficamente seguro**.
- Ele implementa `encoding.TextMarshaler`/`TextUnmarshaler`/`TextAppender`, então se encaixa diretamente em structs JSON.
- **`NewV7` é o que você quer para chaves primárias de banco de dados.** Ordenação temporal significa que o seu índice B-tree deixa de se fragmentar, algo em que UUIDs v4 são notoriamente ruins.

Agora você pode eliminar uma dependência. Se precisar de v1/v3/v5 ou de opções de parsing mais sofisticadas, bibliotecas de terceiros ainda têm utilidade.

### 3.3. crypto/mldsa: assinaturas pós-quânticas, e são enormes

O Go 1.24 nos deu `crypto/mlkem` para troca de chaves pós-quântica. O Go 1.27 traz a outra metade: assinaturas ML-DSA, padronizadas como FIPS 204.

```go
sk, _ := mldsa.GenerateKey(mldsa.MLDSA65())
pk := sk.PublicKey()

msg := []byte("release the gophers")
opts := &mldsa.Options{Context: "gosuda.org/blog"}

sig, _ := sk.Sign(rand.Reader, msg, opts)

fmt.Println("private key seed:", len(sk.Bytes()), "bytes")  // 32
fmt.Println("public key:", len(pk.Bytes()), "bytes")        // 1952
fmt.Println("signature:", len(sig), "bytes")                // 3309

fmt.Println(mldsa.Verify(pk, msg, sig, opts))
// <nil>
fmt.Println(mldsa.Verify(pk, msg, sig, &mldsa.Options{Context: "other"}))
// mldsa: invalid signature
```

Observe esses números. **Uma única assinatura tem 3.309 bytes.** Ed25519 tem 64. É um aumento de cerca de 50 vezes, e a chave pública tem quase 2KB além disso. Coloque algumas dessas em uma cadeia de certificados e o seu handshake TLS passará a precisar de uma estratégia própria de MTU.

Esse é o custo real da resistência quântica hoje, e é por isso que ninguém vai migrar tudo amanhã. Mas ela já está na biblioteca padrão, que é onde você quer que ela esteja _antes_ de precisar dela.

`Options.Context` é separação de domínio: assine com a mesma chave para finalidades diferentes, use um contexto diferente para cada uma, e uma assinatura de um contexto não será verificada em outro. O exemplo acima mostra exatamente isso: mesma chave, mesma mensagem, contexto diferente, rejeitado.

`PrivateKey` implementa `crypto.Signer`, então se encaixa nas interfaces existentes. `crypto/x509` trata chaves e assinaturas ML-DSA, e `crypto/tls` oferece suporte aos esquemas de assinatura `MLDSA44`/`MLDSA65`/`MLDSA87` no TLS 1.3.

Também existe `SignDeterministic`, que dispensa a aleatoriedade, útil para testes e builds reprodutíveis.

### 3.4. simd: instruções vetoriais sem assembly

O Go 1.26 introduziu o `simd/archsimd`, específico de arquitetura, como experimento. O Go 1.27 adiciona `simd`, **portável e agnóstico quanto à largura do vetor**. Ative com `GOEXPERIMENT=simd`.

```go
// dst = a*x + y
func axpy(dst, x, y []float32, a float32) {
	va := simd.BroadcastFloat32s(a)
	w := va.Len()

	i := 0
	for ; i+w <= len(x); i += w {
		vx := simd.LoadFloat32s(x[i:])
		vy := simd.LoadFloat32s(y[i:])
		vx.MulAdd(va, vy).Store(dst[i:])
	}
	// trata a cauda com um carregamento/armazenamento parcial, sem loop escalar de limpeza
	if i < len(x) {
		vx, _ := simd.LoadFloat32sPart(x[i:])
		vy, _ := simd.LoadFloat32sPart(y[i:])
		vx.MulAdd(va, vy).StorePart(dst[i:])
	}
}
```

```bash
$ GOEXPERIMENT=simd go1.27rc3 run ./simddemo
vector bits: 128 emulated: false
[4 7 10 13 16 19 22 25 28 31]
```

A escolha de design que importa: **a largura do vetor nunca é fixa no código.** Você consulta `va.Len()` em tempo de execução. `VectorBitSize()` informa a largura real, e `Emulated()` informa se você obteve hardware real ou uma imitação educada em software. O meu Mac reportou NEON de 128 bits; uma máquina com AVX-512 reporta mais; uma máquina sem nada reporta emulação, e o código continua funcionando.

`LoadFloat32sPart`/`StorePart` merecem menção especial. A parte mais irritante do SIMD escrito à mão é sempre a cauda irregular no final do array, e isso resolve o problema sem um loop escalar separado.

Ainda é experimental, a API ainda é instável, não coloque em produção. Mas o fato de isso ser código Go, e não assembly ou cgo, é uma mudança realmente grande.

### 3.5. hash/maphash.Hasher

Uma nova interface que descreve o contrato entre um tipo de valor e contêineres baseados em hash:

```go
type Hasher[T any] interface {
	Hash(*Hash, T)
	Equal(x, y T) bool
}
```

Por quê? O mapa embutido do Go só aceita chaves `comparable`. Você não pode usar um slice como chave. Não pode definir "igual ignorando maiúsculas e minúsculas". O `Hasher` resolve os dois problemas de uma vez:

```go
type CaseInsensitive struct{}

func (CaseInsensitive) Hash(h *maphash.Hash, s string) {
	h.WriteString(strings.ToLower(s))
}
func (CaseInsensitive) Equal(x, y string) bool {
	return strings.ToLower(x) == strings.ToLower(y)
}

var seed = maphash.MakeSeed()

func hashOf[T any](hr maphash.Hasher[T], v T) uint64 {
	var h maphash.Hash
	h.SetSeed(seed)   // mesma seed, ou nada disso faz sentido
	hr.Hash(&h, v)
	return h.Sum64()
}

fmt.Println(hashOf(CaseInsensitive{}, "Go") == hashOf(CaseInsensitive{}, "GO"))
// true
```

Para a semântica comum de `==`, existe `ComparableHasher[T]`:

```go
hashOf(maphash.ComparableHasher[int]{}, 42)
```

A pegadinha: **`Hasher` é uma interface, não uma estrutura de dados.** Ainda não existe uma tabela hash padrão nem um filtro de Bloom que o consuma. Isso é uma base para um futuro pacote de contêineres. Hoje, você o usaria ao construir sua própria estrutura, ou ao consumir uma implementação existente, como `go/types.Hasher` (que permite usar `types.Type` como chave de mapa, respeitando `Identical`).

O gerenciamento da seed é responsabilidade sua. Se o `hashOf` acima gerasse uma seed nova a cada chamada, valores idênticos receberiam hashes diferentes e o exemplo imprimiria `false`, exatamente o bug que cometi na minha primeira tentativa. Uma seed por contêiner. (A seed é aleatorizada para impedir ataques DoS por inundação de hash, e por isso não é apenas uma constante.)

### 3.6. httptest.NewTestServer + synctest.Sleep: o problema do Sleeper

Se você adotar apenas uma coisa desta versão, que seja esta.

O `testing/synctest` se tornou estável no Go 1.25 e nos deu um relógio falso para testar código concorrente. Só que, no momento em que o teste tocava em uma rede real, a ilusão desmoronava. O `httptest.NewTestServer` do Go 1.27 usa uma **rede falsa em memória**, então a bolha permanece intacta. E `synctest.Sleep` (= `time.Sleep` + `synctest.Wait`) completa o conjunto.

Aqui está um teste de retry com backoff exponencial:

```go
func TestRetryWithBackoff(t *testing.T) {
	synctest.Test(t, func(t *testing.T) {
		var hits int
		srv := httptest.NewTestServer(t, http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				hits++
				if hits < 3 {
					w.WriteHeader(http.StatusServiceUnavailable)
					return
				}
				io.WriteString(w, "ok")
			}))

		client := srv.Client()
		start := time.Now()
		var body string
		for attempt := range 5 {
			resp, err := client.Get(srv.URL)
			if err != nil {
				t.Fatal(err)
			}
			b, _ := io.ReadAll(resp.Body)
			resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				body = string(b)
				break
			}
			synctest.Sleep(time.Duration(1<<attempt) * time.Second)
		}

		if body != "ok" {
			t.Fatalf("got %q", body)
		}
		// o backoff deve ser exatamente 1s + 2s = 3s
		if elapsed := time.Since(start); elapsed != 3*time.Second {
			t.Fatalf("elapsed = %v, want 3s", elapsed)
		}
		t.Logf("hits=%d elapsed=%v", hits, time.Since(start))
	})
}
```

```
=== RUN   TestRetryWithBackoff
    x_test.go:48: hits=3 elapsed=3s
--- PASS: TestRetryWithBackoff (0.00s)
```

Leia de novo. Ele simulou três segundos de backoff contra um servidor HTTP real e terminou em **0,00 segundo**. E a asserção é `elapsed == 3*time.Second`, _exatamente_ três segundos, e não "pelo menos três segundos, com alguma variação do escalonador". Relógios falsos não têm variação.

O `synctest.Sleep` existe por um motivo específico: se o seu teste dorme pela mesma duração que o código testado, não há como saber qual acorda primeiro. O `synctest.Sleep` dorme _e depois_ espera até que todas as outras goroutines da bolha estejam bloqueadas de forma durável, então você observa o sistema depois que ele se acomodou.

Timeouts, retries, circuit breakers, rate limiters: cada teste de cliente HTTP dependente de tempo que você mantém pode se tornar rápido e determinístico. Se a sua suíte de testes hoje se sustenta com `time.Sleep(100 * time.Millisecond)` e esperança, esta é a sua saída.

### 3.7. Mudanças no net/http que afetam produção de fato

Discretas, mas vão aparecer nas suas métricas.

**Os corpos de resposta HTTP/1 são drenados automaticamente no Close.** O conteúdo não lido agora é drenado (até um limite conservador) ao fechar o corpo, para que a conexão possa ser reutilizada. Isso significa que você finalmente pode apagar esta fórmula que todo mundo copiou da mesma resposta do Stack Overflow desde 2016:

```go
// não é mais necessário
defer func() {
	io.Copy(io.Discard, resp.Body)
	resp.Body.Close()
}()
```

Para a maioria dos programas, isso não faz diferença ou é uma pequena melhoria. Se piorar as coisas, você provavelmente está no caso que as notas de lançamento descrevem de forma educada: `Transport.MaxIdleConns` definido como 0, ou um novo `Client` por requisição, contornando totalmente o limite de conexões ociosas. `Transport.DisableKeepAlives = true` esconde o problema, mas o conselho real das notas de lançamento é que "uma análise mais profunda provavelmente seria benéfica", o que, na linguagem da equipe do Go, significa _você tem problemas maiores_.

**Prioridade de cliente HTTP/2 (RFC 9218).** O servidor agora respeita os sinais de prioridade do cliente. Se você preferia o antigo escalonamento round-robin, use `Server.DisableClientPriority = true`.

**`Server.MaxHeaderValueCount`.** Limita quantos valores de cabeçalho o servidor aceitará, com padrão em `DefaultMaxHeaderValueCount`. Mais uma porta fechada para o "envie dez mil cabeçalhos e veja o que acontece".

**ALPN em conexões fornecidas pelo usuário.** Se o seu `net.Conn` implementar `ConnectionState() tls.ConnectionState`, o `Transport` e o `Server` farão a negociação ALPN de TLS sobre ela, de modo que dialers personalizados que passam por um proxy ainda possam negociar HTTP/2.

### 3.8. As pequenas novidades que trazem alegria

**`strings.CutLast` / `bytes.CutLast`.** O `Cut` divide na _primeira_ ocorrência do separador. Não havia uma variante "last", então todo mundo escrevia à mão `LastIndex` mais fatiamento, e cerca de 30% de nós erramos um off-by-one na primeira tentativa.

```go
name, ext, ok := strings.CutLast("archive.tar.gz", ".")
// archive.tar gz true
```

Ótimo para extensões de arquivo e para analisar `host:port`, onde é preciso encontrar os _últimos_ dois-pontos, porque endereços IPv6 estão cheios deles.

**`math/big.Int.Divide`.** Divisão com modo de arredondamento explícito, substituindo a dúvida "é `Quo` ou `Div` que eu quero?":

```go
x, y := big.NewInt(-7), big.NewInt(2)

new(big.Int).Divide(x, y, new(big.Int), big.Trunc)  // q=-3 r=-1
new(big.Int).Divide(x, y, new(big.Int), big.Floor)  // q=-4 r=1
new(big.Int).Divide(x, y, new(big.Int), big.Round)  // q=-4 r=1
new(big.Int).Divide(x, y, new(big.Int), big.Ceil)   // q=-3 r=-1
```

Se você trabalha em um domínio em que a regra de arredondamento está escrita em uma regulamentação, isto é para você.

**`url.URL.Clone` e `url.Values.Clone`.** Cópias profundas. A antiga cópia rasa `*u` compartilhava o ponteiro `Userinfo`, o que produzia exatamente o tipo de bug que leva um dia para ser encontrado e cinco segundos para ser corrigido.

```go
orig, _ := url.Parse("https://gosuda.org/blog?tag=go&tag=1.27")
clone := orig.Clone()
clone.Host = "example.com"
fmt.Println(orig.Host, clone.Host)  // gosuda.org example.com
```

**Os métodos de leitura de `net.UnixConn`** agora retornam `io.EOF` diretamente, em vez de envolvê-lo em `net.OpError`. `err == io.EOF` passa a funcionar, como sempre deveria.

**`database/sql.ConvertAssign` e `driver.RowsColumnScanner`.** Recursos para autores de drivers. O primeiro expõe as conversões de tipo que `Rows.Scan` executa; o segundo permite que drivers façam scan diretamente em destinos do usuário, evitando uma alocação intermediária.

**`unicode` 15 → 17.** Duas versões em um salto. O comportamento de classificação e normalização de strings pode mudar sutilmente. Se você tem testes que dependem disso, vai descobrir.

**`compress/flate` ficou mais rápido**, e **seus bytes de saída podem diferir do Go 1.26.** Isso se propaga para `archive/zip`, `compress/gzip`, `compress/zlib` e `image/png`. Se você tem testes de arquivos de referência (golden files) que calculam hash da saída comprimida, eles vão quebrar, e não será uma regressão. Verifique isso _antes_ de atualizar, e não durante a análise pós-incidente.

## 4. Toolchain

### 4.1. O go fix ganhou mais modernizadores

O `go fix` está silenciosamente se transformando em uma ferramenta de modernização de código. O Go 1.27 adiciona `atomictypes`, `embedlit`, `slicesbackward` e `unsafefuncs`. Use `-diff` para visualizar antes:

```go
package fixdemo

import (
	"sync"
	"sync/atomic"
)

type Base struct{ ID int }
type User struct {
	Base
	Name string
}

func New() User {
	return User{Base: Base{ID: 1}, Name: "gopher"}
}

var counter int64

func Incr() { atomic.AddInt64(&counter, 1) }

func Reverse(s []string) {
	for i := len(s) - 1; i >= 0; i-- {
		_ = s[i]
	}
}

func Spawn(wg *sync.WaitGroup) {
	wg.Add(1)
	go func() {
		defer wg.Done()
	}()
}
```

```bash
$ go1.27rc3 fix -diff ./fixdemo
```

```diff
 import (
+	"slices"
 	"sync"
 	"sync/atomic"
 )

 func New() User {
-	return User{Base: Base{ID: 1}, Name: "gopher"}
+	return User{ID: 1, Name: "gopher"}
 }

-var counter int64
+var counter atomic.Int64

-func Incr() { atomic.AddInt64(&counter, 1) }
+func Incr() { counter.Add(1) }

 func Reverse(s []string) {
-	for i := len(s) - 1; i >= 0; i-- {
-		_ = s[i]
+	for _, v := range slices.Backward(s) {
+		_ = v
 	}
 }

 func Spawn(wg *sync.WaitGroup) {
-	wg.Add(1)
-	go func() {
-		defer wg.Done()
-	}()
+	wg.Go(func() {
+	})
 }
```

Quatro modernizadores foram acionados em um arquivo pequeno:

- `embedlit`: reescreve literais embutidos usando a nova sintaxe da seção 1.2
- `atomictypes`: `atomic.AddInt64(&x, 1)` vira `atomic.Int64.Add(1)`, o que torna impossível o acesso não atômico e corrige silenciosamente bugs de alinhamento em 32 bits que você nem sabia que tinha
- `slicesbackward`: loops reversos viram `slices.Backward`
- `waitgroupgo`: o ritual `Add`/`go`/`Done` vira `wg.Go` (renomeado de `waitgroup`, da 1.26, para evitar ambiguidade)

`go tool fix help` lista todos os 26; `go tool fix help <nome>` explica um deles. Além disso, `fmtappendf` foi removido "por questões de estilo", que é uma maneira belamente diplomática de descrever o que aconteceu naquela discussão da issue.

Executar `go fix ./...` em uma base de código antiga é uma tarde genuinamente satisfatória. Leia o diff primeiro, evidentemente.

### 4.2. O go test executa o stdversion por padrão

Esta é a mudança com mais chances de interromper o seu dia.

O `go test` agora executa a verificação `stdversion` do vet por padrão, sinalizando símbolos da biblioteca padrão mais novos do que a diretiva `go` do seu `go.mod` permite:

```go
// go.mod diz: go 1.24
package sv

import "strings"

func Ext(name string) string {
	_, ext, _ := strings.CutLast(name, ".")  // CutLast é um símbolo da 1.27
	return ext
}
```

```
$ go1.27rc3 test ./...
# sv
./x.go:6:23: strings.CutLast requires go1.27 or later (module is go1.24)
FAIL	sv [build failed]
```

Isso elimina o clássico modo de falha em que tudo funciona na sua máquina (toolchain mais recente) e explode no CI ou no ambiente antigo de um usuário. Se você publica bibliotecas com uma diretiva `go` conservadora, isso é um presente.

### 4.3. go test -json ganhou OutputType

As linhas `"Action":"output"` agora podem trazer um campo opcional `"OutputType"`: `"error"`, `"error-continue"` ou `"frame"`.

```
'frame'  '=== RUN   TestFail\n'
None     '    x_test.go:6: hello\n'
'error'  '    x_test.go:7: boom\n'
'frame'  '--- FAIL: TestFail (0.00s)\n'
'frame'  'FAIL\n'
```

A saída de `t.Log` (sem campo), a saída de `t.Error` (`error`) e as linhas geradas pelo framework (`frame`) agora são distinguíveis. Quem já escreveu uma expressão regular para descobrir quais linhas da saída de teste são "reais" agora pode apagá-la.

### 4.4. Melhorias no go doc

**Sintaxe `package@version`.** Leia a documentação de uma versão específica sem adicioná-la ao seu módulo:

```bash
$ go1.27rc3 doc golang.org/x/sync/errgroup@v0.10.0
package errgroup // import "golang.org/x/sync/errgroup"
...
```

Útil para verificar o que mudou antes de uma atualização.

**`-ex` e impressão do código-fonte dos exemplos:**

```bash
$ go1.27rc3 doc -ex strings | grep Example
    func ExampleClone()
    func ExampleCompare()
    func ExampleContains()
    func ExampleCut()
    func ExampleCutPrefix()
    ...

$ go1.27rc3 doc strings.ExampleCut
package main

import (
	"fmt"
	"strings"
)

func main() {
	show := func(s, sep string) {
		before, after, found := strings.Cut(s, sep)
		fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found)
	}
	show("Gopher", "Go")
	...
}

Output:
Cut("Gopher", "Go") = "", "pher", true
```

O código-fonte do exemplo _e_ a saída esperada, no terminal, sem precisar abrir uma aba do navegador que ainda estará aberta na próxima quinta-feira.

### 4.5. O go mod tidy organiza seus blocos require

Para módulos com `go 1.27` ou posterior, o `go mod tidy` mescla blocos `require` espalhados em no máximo dois (diretos e indiretos), preservando os comentários associados.

```go
// Antes
module tidydemo

go 1.27

require golang.org/x/sync v0.10.0

// networking
require golang.org/x/net v0.33.0

require (
	golang.org/x/sys v0.28.0 // indirect
)

require golang.org/x/text v0.21.0 // indirect
```

```go
// Depois: go mod tidy
module tidydemo

go 1.27

require (
	// networking
	golang.org/x/net v0.33.0
	golang.org/x/sync v0.10.0
)
```

Observe que o comentário `// networking` sobreviveu. Isso ajuda principalmente a limpar depois da **resolução de conflitos de merge no Git**, que é onde os blocos `require` soltos nascem. Se a sua equipe tem mais de três pessoas adicionando dependências, os conflitos no seu `go.mod` vão ficar bem mais silenciosos.

### 4.6. Detalhes diversos

- **Suporte a `bzr` removido.** Se isso afetar você, eu realmente gostaria de ouvir a história.
- **`go tool trace -http` vincula-se ao localhost** quando recebe apenas uma porta. `-http=:6060` não escuta mais em todas as interfaces. Use `-http=0.0.0.0:6060` se essa for a intenção. Isso agora fica alinhado com o `go tool pprof` e evita o eventual profiler público acidental.
- **Arquivos de resposta (`@file`)** são suportados por `compile`, `link`, `asm`, `cgo`, `cover` e `pack`, em um formato compatível com o GCC. Para sistemas de build que estouram os limites de tamanho da linha de comando, olá, Bazel.

## 5. Compilador, linker, portes

**Compilador.** Nomes de arquivo relativos em diretivas `//line` agora são resolvidos em relação ao diretório do arquivo que as contém, em consonância com o `go/scanner`. Relevante se você escreve geradores de código.

Os nomes de símbolos de função literais (closures) também ficaram mais simples: o mesmo nome independentemente de inlining, e múltiplas instâncias do mesmo literal podem compartilhar código no binário. Sem mudança funcional, _exceto_: código que compara a identidade de funções via `reflect.Value.Pointer` vai encontrar "igual" com mais frequência do que antes. Essa comparação nunca foi válida, mas se você a usa, agora ela começará a mentir para você de forma mais alta.

**Linker.** As novas opções `-macos` e `-macsdk` definem as versões do sistema operacional e do SDK no comando de carregamento `LC_BUILD_VERSION` do macOS.

**Portes.**

- **O Darwin agora exige macOS 13 Ventura ou posterior**, como anunciado na 1.26. Verifique os seus runners de CI.
- **PowerPC (`GOOS=linux GOARCH=ppc64`) mudou para a ABI ELFv2.** Requer kernel Linux 3.13+ (o RHEL7 fez backport para 3.10). Cgo, PIE e linkagem externa agora são suportados. Se você usa cgo, mas precisa de um binário Go puro e estático, defina `CGO_ENABLED=0`.

## 6. Checklist de atualização

O Go 1.27 leva a compatibilidade a sério, mas verifique estes pontos antes de atualizar a versão:

- [ ] **Testes com arquivos de referência sobre saída comprimida.** O `compress/flate` mudou os codificadores; os bytes de gzip/zip/png podem ser diferentes.
- [ ] **Testes que comparam strings de mensagens de erro JSON.** O comportamento é idêntico; **o texto do erro não é.**
- [ ] **GODEBUGs removidos no `go.mod`.** `asynctimerchan`, `gotypesalias`, `tlsrsakex`, `tls3des`, `tls10server`, `tlsunsafeekm`, `x509keypairleaf`: estes só fazem o build falhar se estiverem definidos com seus valores _antigos_.
- [ ] **Violações do `stdversion`.** O `go test` agora as detecta. Execute-o cedo se você mantém uma biblioteca com uma diretiva `go` conservadora.
- [ ] **Runners de CI com macOS 12 ou anterior.** O suporte acabou.
- [ ] **Testes que fazem asserções sobre nomes de símbolos de funções literais**, e **comparações de funções com `reflect.Value.Pointer`.**
- [ ] **`Transport.MaxIdleConns = 0`** ou um `Client` por requisição. Combinado com a drenagem automática dos corpos de resposta, isso pode ficar mais lento.

## Considerações finais

O Go 1.27 é a versão em que muita manutenção adiada chegou de uma só vez.

Métodos genéricos eram a peça que faltava no trabalho de genéricos da 1.18. Seletores de campos em literais de struct fecharam uma issue de onze anos. O `encoding/json/v2` foi dois anos de discussão de proposta. E o `uuid` preencheu uma lacuna que praticamente todo projeto Go vinha remendando com a mesma dependência de terceiros há uma década.

Se você quer uma ordem de prioridade para realmente extrair valor disso:

1. **`httptest.NewTestServer` + `synctest.Sleep`**: testes HTTP dependentes de tempo tornam-se rápidos e determinísticos. A melhor relação esforço-retorno de toda a versão.
2. **O perfil de vazamento de goroutines**: exponha um endpoint em staging e prepare-se para se sentir humilhado.
3. **Rótulos de goroutine nos tracebacks**: três linhas de `pprof.Do` no ponto de entrada das requisições transformam o seu próximo dump de pilha às 3 da manhã em algo legível.
4. **`go fix ./...`**: deixe a ferramenta fazer a modernização.
5. **`encoding/json/v2`**: sem pressa. Adote uma opção por vez.

Pegue o `go1.27rc3` e execute sua suíte de testes com ele agora. Tudo nesta lista de verificação é muito mais agradável de descobrir numa terça-feira à tarde do que durante um incidente.

**Referências**

- [Notas de lançamento do Go 1.27](https://go.dev/doc/go1.27)
- [A Especificação da Linguagem de Programação Go](https://go.dev/ref/spec)
- [proposal: encoding/json/v2 (#71497)](https://go.dev/issue/71497)
- [proposal: portable SIMD (#78902)](https://go.dev/issue/78902)
- [proposal: hash/maphash.Hasher (#70471)](https://go.dev/issue/70471)
