slices.Contains vs slices.Index vs slices.IndexFunc: Comparing Go Slice Search Operations
The slices.Contains, slices.Index, and slices.IndexFunc functions from golang.org/x/exp/slices provide idiomatic, O(n) slice lookups—returning a boolean, an index by equality, or an index by predicate, respectively—eliminating the need for manual search loops.
Modern Go development recommends replacing hand-written search loops with standard library helpers. According to the JetBrains/go-modern-guidelines repository, these three functions offer distinct approaches to slice membership testing and element location, each optimized for specific search requirements while maintaining linear time complexity.
Function Comparison: Membership, Position, and Predicate Matching
The golang.org/x/exp/slices package provides three complementary functions that iterate over a slice once and stop at the first match. All three run in O(n) time, but they differ in their return types and matching logic.
slices.Contains: Boolean Membership Testing
slices.Contains(slice []E, elem E) bool reports whether elem appears at least once in the slice. This function is ideal when you only need to verify existence without caring about the element's position.
According to the JetBrains/go-modern-guidelines source code, the repository explicitly recommends using this helper as a replacement for manual search loops. The guideline entry "Use slices.Contains instead of a manual search loop" appears in internal/guidelines/guidelines.json at lines 816–818, with additional mention in the README.md at line 7.
slices.Index: Finding Positions by Equality
slices.Index(slice []E, elem E) int returns the index of the first occurrence of elem or -1 if the element is absent. Use this when you need the position to perform subsequent operations like removal, replacement, or accessing adjacent elements.
While the repository does not maintain a separate guideline entry specifically for slices.Index, the same modernization principles apply—prefer this standard helper over custom for … range constructs that track indices manually.
slices.IndexFunc: Flexible Predicate-Based Search
slices.IndexFunc(slice []S, f func(S) bool) int evaluates a user-supplied predicate function against each element, returning the first index where f(element) returns true, or -1 if no match occurs. This enables complex matching conditions beyond simple equality, such as checking struct fields, derived values, or custom business rules.
The JetBrains guidelines specifically endorse this pattern in internal/guidelines/guidelines.json at lines 866–868 with the entry "Use slices.IndexFunc to find an element by predicate," demonstrating its utility for non-trivial search conditions.
Source Code Recommendations and Complexity
All three functions share identical performance characteristics: they traverse the slice sequentially and terminate immediately upon finding a match. This short-circuit behavior ensures optimal performance even for large datasets when matches appear early.
The internal/guidelines/guidelines.json file serves as the authoritative source for these recommendations, while FEATURES.md (lines 1110–1131) provides concrete implementation examples demonstrating slices.Contains in production scenarios.
Practical Implementation Example
The following example demonstrates all three functions working with different data types and search requirements:
package main
import (
"fmt"
"golang.org/x/exp/slices"
)
type Item struct {
ID int
Name string
}
func main() {
items := []int{3, 7, 9, 12}
people := []Item{
{ID: 1, Name: "Alice"},
{ID: 2, Name: "Bob"},
{ID: 3, Name: "Carol"},
}
// slices.Contains - membership test
hasSeven := slices.Contains(items, 7)
fmt.Printf("contains 7? %v\n", hasSeven)
// slices.Index - find position by equality
idx := slices.Index(items, 9)
fmt.Printf("index of 9: %d\n", idx)
// Not found returns -1
idx = slices.Index(items, 5)
fmt.Printf("index of 5: %d\n", idx)
// slices.IndexFunc - find by custom predicate
idx = slices.IndexFunc(people, func(p Item) bool { return p.Name[0] == 'B' })
if idx >= 0 {
fmt.Printf("first B-named person: %+v\n", people[idx])
}
}
Output:
contains 7? true
index of 9: 2
index of 5: -1
first B-named person: {ID:2 Name:Bob}
Summary
slices.Containsreturns aboolfor simple membership tests, replacing manual loops that only check for existence.slices.Indexreturns anintrepresenting the first matching index (or-1), useful when you need to know where an element resides.slices.IndexFuncreturns anintbased on a predicate function, enabling complex matching logic on struct fields or computed values.- All functions reside in
golang.org/x/exp/slices, operate in O(n) time, and short-circuit at the first match. - The JetBrains/go-modern-guidelines repository explicitly recommends these helpers over hand-written loops in
internal/guidelines/guidelines.json.
Frequently Asked Questions
When should I use slices.Index instead of slices.Contains?
Use slices.Index when you need the position of an element, not just whether it exists. If you need to remove the element, update it in place, or access neighboring values, slices.Index provides the necessary integer index. Use slices.Contains only for boolean membership tests where the position is irrelevant.
How does slices.IndexFunc differ from a manual for loop with a break?
slices.IndexFunc provides the same O(n) short-circuit behavior as a manual loop but eliminates boilerplate and reduces error risk. The function encapsulates the iteration logic, boundary checking, and early return pattern, making the code more readable and less prone to off-by-one errors compared to explicit for i, v := range slice constructs with conditional breaks.
Are these slice search functions part of the Go standard library?
No, they reside in the golang.org/x/exp/slices experimental package, not the standard library slices package introduced in Go 1.21+. The JetBrains/go-modern-guidelines repository specifically references this expansion package. While similar functions exist in the standard library as of newer Go versions, the guidelines target the x/exp implementation for broader compatibility with older Go versions.
What is the time complexity of searching with these helpers?
All three functions operate in O(n) linear time with O(1) space complexity. They iterate through the slice exactly once, stopping immediately upon finding the first match. In the worst-case scenario (element not present or at the end), they examine every element exactly once.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →