Let's say I want to write a function that finds a value in a slice
I intuitively want to write:
func find(s []interface{}, f func(interface{})bool) int {
for i, item := range s {
if f(item) {
return i
}
}
return -1
}
however I don't manage to do this with Go. I could have an interface with
Len() int
Value(int) interface{}
...
and this would work but in my real code things are more complicated (I need to do slices[from:end] etc), append, ... etc and if I redefine all this in an interface I end up having a lot of code. Is there a better way?
Seeing as all of the answers here were written before go added generics, I'll add how you would implement this in go1.18+
Note, however, that as of go1.21, there is a slices
package that contains many helpful, generic slice functions.
This is the implementation of IndexFunc
straight from the standard library
// IndexFunc returns the first index i satisfying f(s[i]),
// or -1 if none do.
func IndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
for i := range s {
if f(s[i]) {
return i
}
}
return -1
}
Notice how the function uses the type parameters [S ~[]E, E any]
instead of just S []any
. The difference is due do how slices are typed in go. Just like how you can't assert a slice of one type to a slice of another even if it's possible to assert the elements (ex. []int{}.([]any)
is invalid even though int
satisfies any
), you can't use an []int
as a parameter of type S
if S
is constrained to []any
.
Instead, it uses S ~[]E
to constrain S
to be a slice with elements of type E
, where E
can be any type. Additionally, this allows functions to use the element type separately, which you can see with the second argument: f func(E) bool
.