slices.BinarySearch returns more than a membership result. Its index identifies the earliest matching position when a target exists, and the position where that target belongs when it does not. That dual contract makes the function useful for maintaining sorted data as well as querying it.
The standard-library signature accepts ordered element types:
func BinarySearch[S ~[]E, E cmp.Ordered](x S, target E) (int, bool)The input must already be sorted in increasing order. The function does not sort, copy, or mutate the slice.
The index remains meaningful when no match exists
A missing target does not produce -1. Instead, the returned index marks the position that preserves ordering if the target is inserted there.
package main
import (
"fmt"
"slices"
)
func main() {
values := []int{10, 20, 40, 50}
index, found := slices.BinarySearch(values, 30)
fmt.Println(index, found) // 2 false
}Index 2 sits between 20 and 40, exactly where 30 belongs. A target smaller than every element produces index 0; a target larger than every element produces len(values).
This means callers should not index the slice blindly. When found is false, the returned index can equal the slice length.
index, found := slices.BinarySearch(values, 80)
fmt.Println(index, found) // 4 falseThe Boolean separates an actual match from a valid insertion boundary.
Duplicate values resolve to the earliest position
For a sorted slice containing repeated values, BinarySearch returns the earliest position at which the target is found.
values := []int{3, 7, 7, 7, 12}
index, found := slices.BinarySearch(values, 7)
fmt.Println(index, found) // 1 trueThat behavior gives the result a lower-bound interpretation: the index is the first position whose value is not less than the target. If that value equals the target, found is true.
The same boundary is useful when insertion policy places a new equal value before an existing run. Code that needs the position after all equal values requires an additional search condition or a separate scan across the equal run.
Sorted order is part of the call contract
Binary search depends on a monotonic ordering boundary. Passing an unsorted slice breaks that premise, so the returned pair no longer represents a valid search result for the data as a whole.
values := []int{10, 40, 20, 50}
index, found := slices.BinarySearch(values, 20)The function does not validate the complete slice before searching it. If sortedness is uncertain and correctness depends on it, that property must be established elsewhere. slices.IsSorted can report whether an ordered slice is in increasing order, while slices.Sort can establish that order when mutation is acceptable.
Sorting solely to perform one search also changes the shape of the operation. Sorting reorders the input and has a different cost profile from scanning an unsorted slice once. BinarySearch fits most naturally when sorted order already exists or serves additional operations.
Search and insertion can share one boundary
The insertion index can feed directly into an operation that maintains sorted order.
values := []int{10, 20, 40, 50}
target := 30
index, found := slices.BinarySearch(values, target)
if !found {
values = slices.Insert(values, index, target)
}
fmt.Println(values) // [10 20 30 40 50]The search establishes the ordering boundary; slices.Insert performs the structural change. The two operations have different mutation semantics: BinarySearch only reads the slice, while insertion can reuse or replace its backing storage and returns the resulting slice value.
If duplicates are not permitted, the found result also provides the admission check without a second lookup.
Ordered element semantics define the result
The cmp.Ordered constraint covers types with language-level ordering, including integers, strings, and floating-point types. For floating-point values, NaN requires special attention because ordinary comparisons do not form the same total order as ordinary numeric values.
The slices sorting and search APIs define consistent handling for ordered floating-point slices, including NaN placement. Code that builds the slice through a different ordering rule must ensure the search operation uses a compatible order.
For structs or domain-specific ordering, slices.BinarySearchFunc provides the related operation with an explicit comparison function. Its comparison must describe the same increasing order used to arrange the slice.
The boundary is often the useful result
Membership is only one interpretation of binary search. In sorted collections, the returned boundary can drive insertion, duplicate handling, range construction, and partition logic without a second traversal to rediscover the same position.
That makes the precondition as significant as the algorithm: the index is reliable only while the slice and the search function agree on a single increasing order.