Finding the largest value in a slice is a small operation, but handwritten loops still create room for awkward initialization and missed empty-input handling. For ordered values such as integers, strings, and floating-point numbers, slices.Max expresses the operation directly.
The function scans the slice and returns its maximal element. It doesn’t sort or modify the input. Two details deserve attention in production code: an empty slice causes a panic, and floating-point input containing a NaN produces a NaN result.
Select the largest value directly
For a non-empty slice of integers, the call is straightforward:
package main
import (
"fmt"
"slices"
)
func main() {
latencies := []int{18, 42, 7, 31}
highest := slices.Max(latencies)
fmt.Println(highest)
}The output is:
42The original order remains unchanged. slices.Max only reads the elements while selecting the maximal value.
The same operation works with other ordered types. Strings, for example, use their normal lexical ordering:
names := []string{"delta", "alpha", "omega"}
last := slices.Max(names)
fmt.Println(last) // omegaThat behavior is useful when the ordering already matches the program’s requirement. If the domain needs a custom rule, such as comparing structs by a field, slices.MaxFunc is the more suitable helper.
Max does not require sorted input
A common unnecessary step is sorting before selecting one extreme value:
slices.Sort(latencies)
highest := latencies[len(latencies)-1]That changes the slice and performs more work than the selection requires. slices.Max can inspect unsorted input directly:
highest := slices.Max(latencies)Use sorting when the ordered sequence itself is needed afterward. Use Max when the result is just the largest element.
This distinction also makes intent clearer during review. A reader seeing slices.Max(values) doesn’t have to determine whether a preceding sort exists only to access the final element.
Guard empty slices before calling Max
slices.Max requires at least one element. Calling it with an empty slice panics:
values := []int{}
_ = slices.Max(values)That contract is reasonable because there is no universal maximal value for an empty collection. Returning zero would be misleading: zero could be a valid element, and it isn’t larger than every possible integer.
When empty input is valid in the surrounding application, handle it before the call. A small wrapper can make the policy explicit:
func maxInt(values []int) (int, bool) {
if len(values) == 0 {
return 0, false
}
return slices.Max(values), true
}Callers can then distinguish a real maximum of zero from the absence of any value:
highest, ok := maxInt(readings)
if !ok {
fmt.Println("no readings")
return
}
fmt.Println(highest)Another application may prefer returning an error or skipping the calculation entirely. The right choice belongs to that API’s empty-input policy rather than to slices.Max.
Floating-point NaN values propagate
Floating-point data needs one extra consideration. If any element is NaN, slices.Max returns NaN.
package main
import (
"fmt"
"math"
"slices"
)
func main() {
samples := []float64{2.5, math.NaN(), 8.1}
highest := slices.Max(samples)
fmt.Println(math.IsNaN(highest))
}The output is:
trueThis differs from silently ignoring invalid measurements. If NaN means missing or unusable data in your domain, filter or reject those values before calling Max.
For example:
clean := make([]float64, 0, len(samples))
for _, value := range samples {
if !math.IsNaN(value) {
clean = append(clean, value)
}
}
if len(clean) != 0 {
highest := slices.Max(clean)
fmt.Println(highest)
}Filtering also creates a second empty-input case: every original value might be NaN. Check the filtered slice before selecting its maximum.
Prefer Max over a fragile manual loop
A manual maximum loop is sometimes appropriate, especially when selection is combined with other work. The risky version starts with a made-up default:
highest := 0
for _, value := range values {
if value > highest {
highest = value
}
}That fails when every value is negative. For []int{-9, -4, -12}, it incorrectly leaves highest at zero.
A correct manual loop needs a non-empty precondition and should initialize from an actual element:
if len(values) == 0 {
return
}
highest := values[0]
for _, value := range values[1:] {
if value > highest {
highest = value
}
}slices.Max packages that common operation behind a standard-library name. Keep the explicit loop when you also need the index, want to gather several statistics in one pass, or have selection rules that don’t fit normal ordering.
Choose the helper that matches the data
slices.Max is intended for ordered element types. It fits values such as integers, floating-point numbers, and strings when their normal ordering is the desired rule.
For structs or domain-specific ordering, use slices.MaxFunc with a comparator. If both the minimum and maximum are required and the surrounding code already scans the slice for other statistics, one explicit pass can be easier to extend than separate helper calls.
The built-in max function is another nearby option, but it operates on a fixed set of arguments rather than accepting a slice directly. For a collection already stored as []int, []string, or another ordered slice, slices.Max avoids expanding or manually traversing that collection.
Keep the precondition visible
slices.Max is most useful when the collection is already known to contain at least one ordered value. It selects the largest element without reordering the slice and keeps a routine operation compact.
At boundaries where empty input can occur, check it explicitly. For floating-point collections, decide how NaN should be treated before selection. With those policies made visible, slices.Max stays simple and predictable.