Contains in Go for slices: how to check and not go crazy

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

"Where is contains?" — the first pain point for a beginner in Go

\n\n

Imagine: you're writing in Go, you have a slice of strings ["python", "go", "rust", "javascript"], and you need to check if the language "go" is in it. If you came from Python, Ruby, or JavaScript, you automatically write slice.contains("go") — and get a red compilation error. "What nonsense is this?" you think. "Every self-respecting collection has a contains method!"

\n\n

Calm down, just calm down. Go really does not have a built-in contains method for slices. And this is not a bug, but a feature. The language developers decided that one universal function would not cover all cases (comparing strings, numbers, structs), and left the implementation to the programmer. It sounds scary, but in reality it's simple and elegant. In this article, we'll break down how to write contains for a slice in Go, look at performance, typical pitfalls, and show ready-made solutions.

\n\n

Why is there no contains for a slice in Go?

\n

Go is a minimalist language. It has no generics (before version 1.18) and no method overloading. A slice is not an object with methods, but a data structure: a pointer to an array, length, and capacity. Go developers decided that the contains function should be explicit, so the programmer always understands exactly how the comparison happens. This reduces the number of bugs and makes the code predictable.

\n

Furthermore, contains for a slice is always a linear search (O(n)), and if you expect O(1), you are using the wrong data structure (you need a map). Go does not want to mask this complexity.

\n\n

The simplest contains for a slice in Go (manual iteration)

\n

Let's write contains for a slice of strings. This is the classic you will see in any production code:

\n\n
package main\n\nimport "fmt"\n\n// Contains checks if an element is in a string slice\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\n

It's simple: we loop through the slice, comparing each element with the target. If we find it — true, otherwise false. Important: the == operator works for strings, numbers, boolean values, and pointers. For structs or slices within slices (nested), this won't work.

\n\n

Contains for numbers

\n

For a slice of integers, the code is almost identical:

\n\n
func 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

Inconvenient? Yes. But this is Go — explicitness and simplicity above all.

\n\n

Universal contains for a slice in Go (generics, Go 1.18+)

\n

With the release of Go 1.18, generics appeared in the language. Now you can write one contains function for any type that supports comparison:

\n\n
package main\n\nimport "fmt"\n\n// ContainsGeneric works for any comparable type\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\n

The comparable constraint means the type supports the == and

Blogs

Book Recommendations