Represent and transform data safely

Find an Element in a Go Slice with `slices.Index` or a Loop

Find the first matching index in a Go slice, check membership, or search by predicate with standard-library helpers and simple loops.

By Niko Minadze4 min read

Editorial illustration for Find an Element in a Go Slice with `slices.Index` or a Loop

If you need an element’s position in a slice, use slices.Index(slice, value). It returns the first index where the value occurs, or -1 if it is absent. If you only need a yes/no answer, use slices.Contains instead. For a condition such as “the first value greater than 10,” a short loop makes the predicate and its result explicit.

The standard-library slices.Contains is available in Go 1.21 and later. If the standard-library helper you need is unavailable in your toolchain, or the search needs custom logic, use a loop.

Get the first equal element with slices.Index

This complete example shows a present value, an absent value, a value that appears twice, and two predicate searches:

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{7, 4, 7, 12}

    fmt.Println("first 7:", slices.Index(numbers, 7))
    fmt.Println("12:", slices.Index(numbers, 12))
    fmt.Println("9:", slices.Index(numbers, 9))

    fmt.Println("contains 7:", slices.Contains(numbers, 7))
    fmt.Println("contains 9:", slices.Contains(numbers, 9))

    fmt.Println("first > 10:", firstIndexWhere(numbers, func(n int) bool {
        return n > 10
    }))
    fmt.Println("first < 0:", firstIndexWhere(numbers, func(n int) bool {
        return n < 0
    }))
}

func firstIndexWhere(numbers []int, match func(int) bool) int {
    for i, n := range numbers {
        if match(n) {
            return i
        }
    }
    return -1
}

The results are:

first 7: 0
12: 3
9: -1
contains 7: true
contains 9: false
first > 10: 3
first < 0: -1

The second 7 is at index 2, but Index returns 0 because it finds the first occurrence. The Go team’s implementation example shows how: it visits indexes in order, compares each element with ==, and returns immediately on a match. Only after reaching the end does it return -1. Check for -1 before using the result to access the slice; -1 is a search result, not an element’s position.

Index and Contains take values of a comparable element type. If equality is not the test you want, describe the match with a predicate instead. For more on comparable constraints when writing your own generic helper, see Go Generics: How to Write Type Parameters, Constraints, and Generic Interfaces.

Choose a boolean or a predicate when that is the real question

Use slices.Contains(numbers, 7) when the caller does not need an index. It reports whether the value is present, so there is no -1 result to interpret. The standard-library documentation also provides ContainsFunc when the question is whether any element satisfies a predicate; it stops when one does.

When you need the position of a predicate match, firstIndexWhere returns at the first successful call to match. In the example, n > 10 first succeeds at index 3. The n < 0 predicate never succeeds, so the loop reaches return -1. This differs from looking for a particular value: the caller defines what counts as a match. If several elements qualify, stopping immediately preserves the first matching index.

For an unsorted slice, do not substitute slices.BinarySearch just because its name suggests a faster lookup. Its standard-library contract requires increasing sort order and returns both a position and a found boolean. When the value is absent, that position is where it would belong, not evidence that it exists.

Write a loop for an older toolchain or custom logic

When a standard-library search helper is unavailable, an index loop performs the equality search without a slices import:

func firstIndex(numbers []int, wanted int) int {
    for i, n := range numbers {
        if n == wanted {
            return i
        }
    }
    return -1
}

For []int{7, 4, 7, 12}, firstIndex(numbers, 7) returns 0 and firstIndex(numbers, 9) returns -1. Return as soon as the match is found: continuing through the slice would no longer implement a first-index search. This type-specific function also keeps the equality rule visible. If the rule instead depends on a condition, use the predicate loop above or put that condition directly in the loop.

Comments

No comments yet.

Leave a comment

Your comment appears after approval. Plain text only; links stay as text. Line breaks and indentation are kept.

Up to 5,000 characters. No account or email needed.

All articles

Type at least two characters.

Press <kbd>Esc</kbd> to closeOpen the search page