How can I make the RemoveDead
function accept other slices of interfaces (or maybe even slices of struct pointers) that implement Liver
with little impact on performance?
It seems to me that the function would have to take an interface{}
as argument and do runtime conversions, but I'm not sure how to do the conversions. I would also guess that doing x.(Liver)
is a lot slower than Liver(x)
, because the latter is a compile time conversion.
Is the best solution to copy-paste the function and change the argument and return type in each copy? Only three or four copies would be needed, but it would still feel like a very clumsy solution.
type Updater interface {
Liver
Update() bool
}
type Liver interface {
Alive() bool
}
func RemoveDead(all []Updater) []Updater {
for i := len(all) - 1; i >= 0; i-- {
if Liver(all[i]).Alive() == false {
all[i] = all[len(all)-1]
all = all[:len(all)-1]
}
}
return all
}
As you mentioned, a slice of type []Updater
cannot be turned to a []Liver
with a simple type assertion; their type are not interfaces, but slices of interfaces. For the same reason is it not possible to pass a []Updater
to a function wanting an []interface{}
as parameter.
However, you can do what you desire using the reflect
package. Reflection is useful but will come at a cost on performance. If you consider the cost to high, then you will probably have to use the copy-paste solution.
The code below can surely be improved, but it shows how to solve the problem with reflection, and it might be useful when making a benchmark. Currently it regards any even U value as Alive:
package main
import (
"fmt"
"reflect"
)
type Updater interface {
Alive() bool
Update() bool
}
type Liver interface {
Alive() bool
}
type U int
func (u U) Alive() bool { return u % 2 == 0 }
func RemoveDead(all interface{}) interface{} {
v := reflect.ValueOf(all)
if v.Kind() != reflect.Slice {
panic("RemoveDead requires a slice")
}
for i := v.Len() - 1; i >= 0; i-- {
l := v.Index(i)
if l.Interface().(Liver).Alive() == false {
l.Set(v.Index(v.Len()-1))
v = v.Slice(0, v.Len()-1)
}
}
return v.Interface()
}
func main() {
u := []U{1,4,7,2,12}
fmt.Println("Before: ", u)
u = RemoveDead(u).([]U)
fmt.Println("After: ", u)
}
Output:
Before: [1 4 7 2 12]
After: [2 4 12]
You could define third interface:
type UpdaterLiver interface {
Updater
Liver
}
then change definition of RemoveDead
to be
func RemoveDead(all []UpdaterLiver) []UpdaterLiver