# slices.Contains vs slices.Index vs slices.IndexFunc: Comparing Go Slice Search Operations

> Compare Go's slices Contains Index and IndexFunc for efficient slice searching. Learn to choose the right O(n) lookup method and avoid manual loops.

- Repository: [JetBrains/go-modern-guidelines](https://github.com/jetbrains/go-modern-guidelines)
- Tags: deep-dive
- Published: 2026-08-30

---

**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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) at lines 816–818, with additional mention in the [`README.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) file serves as the authoritative source for these recommendations, while [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/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:

```go
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.Contains`** returns a `bool` for simple membership tests, replacing manual loops that only check for existence.
- **`slices.Index`** returns an `int` representing the first matching index (or `-1`), useful when you need to know where an element resides.
- **`slices.IndexFunc`** returns an `int` based 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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/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.