Cópia de Cabeçalho de Slice e Surpresa no Append
Duas funções compartilham o que parecem ser slices independentes até que um append sobrescreva os elementos do outro.
Busque em todas as páginas da documentação
Duas funções compartilham o que parecem ser slices independentes até que um append sobrescreva os elementos do outro.
Esta página mostra como a cópia do cabeçalho de slice cria aliasing, como append reutiliza a capacidade e os padrões de cópia que previnem a corrupção de dados em produção.
Um slice Go é um pequeno cabeçalho: ponteiro, comprimento e capacidade.
Atribuição e passagem por valor copiam o cabeçalho, não o array subjacente.
Quando dois cabeçalhos apontam para o mesmo array, append em um slice pode mutar elementos visíveis através do outro se houver capacidade extra disponível.
Esses bugs são insidiosos porque os programas raramente entram em pânico - eles retornam dados errados sob tamanhos e padrões de carga específicos.
Cartão de receita de referência rápida - pronto para copiar e colar.
// Limita a capacidade ao expor uma visualização interna
sub := s[low:high:high] // cap == len, append realoca
// Cópia defensiva antes de passar para o chamador
out := append([]T(nil), s...)
// Copia apenas o prefixo
prefix := append([]T(nil), s[:n]...)Quando usar isso:
[]byte reutilizados entre requisiçõesappend após s[:n] em armazenamento compartilhado[]byte nas camadas HTTP/gRPCpackage main
import "fmt"
func main() {
base := []int{1, 2, 3, 4, 5}
window := base[1:3] // len 2, cap 4 (compartilha o array subjacente)
fmt.Println("antes", base, window, "cap", cap(window))
window = append(window, 99) // escreve no array subjacente de base
fmt.Println("depois ", base, window)
safe := append([]int(nil), base[1:3]...)
safe = append(safe, 99)
fmt.Println("seguro ", base, safe)
}O que isso demonstra:
window compartilha o array de base; append com capacidade extra muta basecap(window) maior que len(window) é o sinal de perigolen, cap e o slice subjacente completo captura o defeito em testesponteiro | len | caps[low:high] define len=high-low, cap=cap(s)-low - frequentemente maior que lenappend reutiliza o array subjacente quando len < cap; caso contrário, aloca um novo array| Objetivo | Padrão |
|---|---|
| Expor visualização somente leitura | Retornar cópia append([]T(nil), s...) |
| Visualização interna sem crescimento de append no pai | Slice de três índices s[i:j:j] |
| Truncar, mas manter cap | s = s[:n:n] antes de passar adiante |
| Resetar buffer reutilizável | s = s[:0] apenas quando for o único proprietário |
// Analisador mantendo um buffer []byte
func parseFrame(buf []byte) (payload []byte, rest []byte) {
n := int(buf[0])
payload = append([]byte(nil), buf[1:1+n]...) // copia o payload
rest = buf[1+n:]
return payload, rest
}append sempre aloca - Ele reutiliza a capacidade quando possível. Correção: verifique cap ou copie primeiro.s[:n] de um buffer interno - Chamadores fazem append no seu pool. Correção: copie ou limite a capacidade com três índices.bytes.Buffer.Bytes() - Bytes permanecem válidos até a próxima mutação do buffer. Correção: copie se for armazenar por muito tempo.s[low:high:high].s = nil antes do unmarshal se precisar de alocação nova.| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Slice de três índices | Visualização zero-copy, sem append pelo consumidor | Consumidor precisa de espaço para append |
append([]T(nil), s...) | Passar a propriedade para o chamador | Caminho quente precisa de zero-copy e o contrato proíbe append |
slices.Clip (Go 1.21+) | Truncar cap para len em uma chamada | Precisa suportar Go mais antigo sem polyfill |
Pool []byte separado por goroutine | Analisadores de alta vazão | Caminhos de código simples onde a cópia é barata |
Slices são visualizações leves.
Copiar o array a cada atribuição destruiria o desempenho.
O compartilhamento é explícito através das regras de capacidade.
Procure por s[low:high] armazenado ou retornado, seguido por append no slice derivado.
Verifique se o slice pai ainda está ativo.
O range sobre um slice copia o valor de cada elemento.
Mutar campos de struct via valor de range não atualiza o slice; atribuição por índice sim.
Strings são imutáveis.
Substrings compartilham bytes subjacentes, mas não podem ter append.
slices.Clone(s) aloca um novo slice com elementos copiados.
Use quando quiser um helper da stdlib em vez de append([]T(nil), s...).
Sim - slices externos e internos têm cabeçalhos.
Copie os slices internos ao passar linhas para trabalho assíncrono.
Mute o slice derivado, afirme que o pai não foi alterado.
Teste em tabela com comprimentos onde cap > len.
Não - apenas o compartilhamento de capacidade afeta os elementos dentro da cap.
O len do pai permanece inalterado, a menos que você atribua de volta à variável pai.
O Reader gerencia a posição, mas o slice subjacente ainda pode ter aliasing.
Copie se os consumidores mantiverem visualizações []byte.
log.Printf("base=%p len=%d cap=%d %#v", &base[0], len(base), cap(base), base) antes e depois do append no slice derivado.
Não - cabeçalhos de slice se comportam da mesma forma para []T com qualquer T.
Parsers de alta performance e pilhas de rede.
Documente que os consumidores não devem fazer append; imponha com cap de três índices ou tipo opaco.
Versões de Stack: Esta página foi escrita para Go 1.26.x (padrão GC Green Tea, go fix modernizers - verifique o patch na compilação), chi (última - verifique na compilação), gin (última - verifique na compilação), echo (última - verifique na compilação), google.golang.org/grpc (última - verifique na compilação), sigs.k8s.io/controller-runtime (última - verifique na compilação), kubebuilder (última - verifique na compilação), tinygo (última - verifique os alvos de placa na compilação), wazero (última - verifique na compilação) e golangci-lint (última - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026