«¿Y dónde está contains?» — el primer dolor del novato en Go
\n\nImagínese: está programando en Go, tiene un slice de strings ["python", "go", "rust", "javascript"] y necesita verificar si el lenguaje "go" está presente. Si viene de Python, Ruby o JavaScript, automáticamente escribe slice.contains("go") — y obtiene un error de compilación en rojo. «¿Qué tontería es esta?» — piensa. «¡En cualquier colección que se respete hay un método contains!»
Tranquilidad, solo tranquilidad. En Go realmente no hay un método contains incorporado para slices. Y esto no es un error, sino una característica. Los desarrolladores del lenguaje decidieron que una función universal no cubriría todos los casos (comparación de strings, números, estructuras) y dejaron la implementación al programador. Suena aterrador, pero en realidad es simple y elegante. En este artículo analizaremos cómo escribir contains para un slice en Go, veremos el rendimiento, los errores típicos y mostraremos soluciones listas.
\n\n¿Por qué no hay contains para slices en Go?
\nGo es un lenguaje minimalista. No tiene genéricos (hasta la versión 1.18) ni sobrecarga de métodos. Un slice no es un objeto con métodos, sino una estructura de datos: un puntero a un array, longitud y capacidad. Los desarrolladores de Go decidieron que la función contains debe ser explícita, para que el programador siempre entienda cómo ocurre la comparación. Esto reduce la cantidad de errores y hace que el código sea predecible.
\nAdemás, contains para un slice siempre es una búsqueda lineal (O(n)), y si espera O(1), está usando la estructura incorrecta (necesita un map). Go no quiere ocultar esta complejidad.
\n\nEl contains más simple para un slice en Go (recorrido manual)
\nEscribamos contains para un slice de strings. Esto es un clásico que verá en cualquier código de producción:
\n\npackage main\n\nimport "fmt"\n\n// Contains verifica si un elemento está en el 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\nMuy simple: recorremos el slice con un bucle, comparamos cada elemento con el buscado. Si lo encontramos — true, si no — false. Importante: el operador == funciona para strings, números, valores booleanos y punteros. Para estructuras o slices dentro de slices (anidados) no funcionará así.
Contains para números
\nPara un slice de números enteros, el código es casi 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\n¿Incómodo? Sí. Pero esto es Go: la claridad y la simplicidad están por encima de todo.
\n\nContains universal para un slice en Go (genéricos, Go 1.18+)
\nCon el lanzamiento de Go 1.18, el lenguaje incorporó los genéricos (generalizaciones). Ahora se puede escribir una única función contains para cualquier tipo que soporte comparación:
\n\npackage main\n\nimport "fmt"\n\n// ContainsGeneric funciona para cualquier 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\nLa restricción comparable significa que el tipo soporta los operadores == y