"Cadê o contains?" — a primeira dor de cabeça de quem está começando em Go
\n\nImagine: você está programando em Go, tem um slice de strings ["python", "go", "rust", "javascript"] e precisa verificar se a linguagem "go" está presente. Se você veio do Python, Ruby ou JavaScript, automaticamente escreve slice.contains("go") — e leva um erro de compilação vermelho na cara. "Que absurdo é esse?", você pensa. "Qualquer coleção que se preze tem um método contains!"
Calma, só calma. No Go, realmente não existe um método contains nativo para slices. E isso não é um bug, é uma feature. Os criadores da linguagem decidiram que uma única função genérica não atenderia a todos os casos (comparação de strings, números, structs) e deixaram a implementação por conta do programador. Parece assustador, mas na prática é simples e elegante. Neste artigo, vamos mostrar como criar um contains para slice em Go, analisar performance, evitar as armadilhas comuns e fornecer soluções prontas.
\n\nPor que Go não tem contains para slice?
\nGo é uma linguagem minimalista. Ela não tinha genéricos (até a versão 1.18) nem sobrecarga de métodos. Um slice não é um objeto com métodos, mas uma estrutura de dados: um ponteiro para um array, um comprimento e uma capacidade. Os criadores do Go optaram por tornar a função contains explícita, para que o programador sempre entenda exatamente como a comparação é feita. Isso reduz bugs e torna o código previsível.
\nAlém disso, contains para um slice é sempre uma busca linear (O(n)). Se você espera O(1), está usando a estrutura de dados errada (precisa de um map). O Go não quer mascarar essa complexidade.
\n\nO contains mais simples para slice em Go (iteração manual)
\nVamos escrever um contains para slice de strings. Este é o clássico que você verá em qualquer código de produção:
\n\npackage main\n\nimport "fmt"\n\n// Contains verifica se um elemento existe em um slice de strings\nfunc Contains(slice []string, target string) bool {\n for _, item := range slice {\n if item == target {\n return true\n }\n }\n return false\n}\n\nfunc main() {\n languages := []string{"python", "go", "rust", "javascript"}\n fmt.Println(Contains(languages, "go")) // true\n fmt.Println(Contains(languages, "csharp")) // false\n}\n\nSimples: percorremos o slice com um loop, comparando cada elemento com o alvo. Se encontrar, retorna true; senão, false. Importante: o operador == funciona para strings, números, booleanos e ponteiros. Para structs ou slices dentro de slices (aninhados), isso não funciona.
Contains para números
\nPara um slice de inteiros, o código é quase idêntico:
\n\nfunc ContainsInt(slice []int, target int) bool {\n for _, v := range slice {\n if v == target {\n return true\n }\n }\n return false\n}\n\nfunc main() {\n numbers := []int{10, 20, 30, 40, 50}\n fmt.Println(ContainsInt(numbers, 30)) // true\n fmt.Println(ContainsInt(numbers, 99)) // false\n}\n\nInconveniente? Sim. Mas isso é Go: clareza e simplicidade acima de tudo.
\n\nContains universal para slice em Go (genéricos, Go 1.18+)
\nCom o lançamento do Go 1.18, a linguagem ganhou genéricos. Agora podemos escrever uma única função contains para qualquer tipo que suporte comparação:
\n\npackage main\n\nimport "fmt"\n\n// ContainsGeneric funciona para qualquer tipo comparable\nfunc ContainsGeneric[T comparable](slice []T, target T) bool {\n for _, item := range slice {\n if item == target {\n return true\n }\n }\n return false\n}\n\nfunc main() {\n strs := []string{"a", "b", "c"}\n ints := []int{1, 2, 3}\n floats := []float64{1.1, 2.2, 3.3}\n\n fmt.Println(ContainsGeneric(strs, "b")) // true\n fmt.Println(ContainsGeneric(ints, 5)) // false\n fmt.Println(ContainsGeneric(floats, 2.2)) // true\n}\n\nA restrição comparable significa que o tipo suporta os operadores == e