slices.EqualFunc compares two slices position by position while leaving the definition of equality to a supplied function. That makes the operation useful when plain == is unavailable or does not express the relation the program needs.
The function also permits the two slices to have different element types:
func EqualFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](
s1 S1,
s2 S2,
eq func(E1, E2) bool,
) boolThe result still describes sequence equality. Length, order, and every corresponding pair matter.
Length is checked before element equality
Slices with different lengths cannot compare equal through EqualFunc. The equality function is relevant only when both slices contain the same number of elements.
left := []string{"api", "worker"}
right := []string{"API"}
equal := slices.EqualFunc(left, right, strings.EqualFold)
fmt.Println(equal) // falseThis contract separates sequence shape from element equivalence. A custom equality function can broaden or narrow the relation between two elements, but it cannot make a two-element sequence equal to a one-element sequence.
Nil and empty slices both have length zero, so they compare equal. EqualFunc does not use slice nilness as part of the result.
Comparison follows index order
For equal-length inputs, comparison begins at index zero and advances until a pair fails the supplied equality function. At that point the function returns false without evaluating later pairs.
left := []string{"alpha", "BETA", "gamma"}
right := []string{"ALPHA", "beta", "GAMMA"}
equal := slices.EqualFunc(left, right, strings.EqualFold)
fmt.Println(equal) // truestrings.EqualFold changes the equality relation for each string pair, but it does not change ordering. Reordering one slice still changes the result:
right = []string{"beta", "ALPHA", "GAMMA"}
fmt.Println(slices.EqualFunc(left, right, strings.EqualFold)) // falseThis makes EqualFunc distinct from set comparison. Duplicate counts and positions remain observable properties of the sequence.
Different element types can share one equality relation
The separate E1 and E2 type parameters allow comparison across representations. A slice of numeric identifiers can, for example, be compared with a slice containing their textual forms.
numbers := []int{16, 32, 64}
text := []string{"16", "32", "64"}
equal := slices.EqualFunc(numbers, text, func(n int, s string) bool {
parsed, err := strconv.Atoi(s)
return err == nil && n == parsed
})
fmt.Println(equal) // trueThe conversion policy belongs inside the equality function. Here an invalid decimal string simply makes its pair unequal. Another API could choose a different policy before calling EqualFunc if parse errors need to remain distinguishable from ordinary inequality.
Cross-type comparison is especially useful at representation boundaries. It can compare decoded values with serialized fields or domain identifiers with transport forms without first allocating a converted copy of an entire slice.
Equality can cover non-comparable element types
Plain slices.Equal requires a comparable element type because it uses Go equality directly. EqualFunc places no comparable constraint on either element type.
That permits slices containing structs with slice fields, maps, or other values that cannot be operands of == as complete values.
type Record struct {
ID int
Flags []string
}
left := []Record{{ID: 7, Flags: []string{"a", "b"}}}
right := []Record{{ID: 7, Flags: []string{"a", "b"}}}
equal := slices.EqualFunc(left, right, func(a, b Record) bool {
return a.ID == b.ID && slices.Equal(a.Flags, b.Flags)
})
fmt.Println(equal) // trueThe callback makes the comparison boundary explicit. Fields can be included, normalized, or ignored according to the actual equality relation required by the surrounding code.
Callback behavior is part of the comparison contract
EqualFunc does not validate mathematical properties of the supplied function. If the callback is asymmetric, stateful, or dependent on external mutable data, the result inherits those properties.
A relation such as approximate floating-point equality illustrates this point. A tolerance-based callback may be suitable for a specific numeric comparison, but the tolerance and treatment of special values are application decisions rather than behavior supplied by EqualFunc itself.
The callback can also have side effects, but short-circuit evaluation makes invocation counts dependent on the data. Code should not rely on the callback running once for every element unless all earlier pairs compare equal.
Custom equality stays narrower than ordering
EqualFunc answers a Boolean sequence-equality question. It does not establish ordering between unequal slices. Code that needs lexicographic ordering has a different contract and can use slices.CompareFunc with a three-way comparison function.
Keeping those relations separate avoids encoding ordering into a predicate that only returns equal or unequal. EqualFunc is most precise when the program already has a clear pairwise equality relation and needs to apply it across two ordered sequences with matching lengths.