Go functions declare parameter types and result types. Multiple return values are a routine way to express related outcomes. A search can return an index and a bool: index zero is perfectly usable when the bool is true. If you know Python tuple returns, the caller experience is familiar, but Go checks both the number and the types of results at compilation. C++ users can think of a small explicit product of results without a custom result struct.
Use named functions to separate a policy from a traversal. Read the function's contract before choosing its loop. Finding the first qualifying element means returning at the first match. Finding every qualifying element would need different storage and a different contract. Code that searches correctly but returns the last match still violates a first-match promise.
One loop keyword, distinct exit operations
for supports a three-part loop, a condition-only loop and range traversal.
For a slice, for i, value := range xs supplies an index and an element value.
The blank identifier discards a result you do not need. This lesson reads slices
as finite sequences; the next lesson examines how they share backing storage.
An empty or nil slice simply has no iterations.
An early return leaves the function. break leaves the innermost applicable loop or switch. continue begins the loop's next iteration. A sentinel that ends a record stream therefore needs break, not continue: discarding one marker and consuming its suffix changes the meaning of the stream. Go does not infer the intended meaning of a sentinel from its value.
package main
import "fmt"
func first(xs []int, threshold int) (int, bool) {
for i, value := range xs {
if value >= threshold { return i, true }
}
return 0, false
}
func main() {
fmt.Println(first([]int{5, 8}, 5))
fmt.Println(first(nil, 5))
}The output is 0 true and 0 false. The same numeric index has a different
interpretation because the validity result differs. Assign both results at the
call site, check the bool, and only then index the sequence.
Small boundaries make the implementation reviewable
Trace a leading sentinel, a trailing sentinel and no sentinel. Include zero: it is not negative. For FirstAtLeast, test an exact threshold tie and repeated matching values. Both are small cases that distinguish an almost-correct comparison from the actual contract. These tasks bound input magnitudes so the lesson can focus on control flow rather than overflow policy.
The build asks for two pure functions with no output or mutation. The fill completes the search predicate. The bug hunts each change one decision: whether ties qualify, and whether a sentinel ends consumption. Treat passing examples as evidence for those decisions, not as a reason to omit the empty case.