> Most newbie gophers don't understand how powerful duck-typed interfaces are
Duck-typed interfaces are kind of broken in Go, though. I can use an Impl anywhere I can use an Abstract, but if I have a function that takes a slice of Abstract, I can't pass it a slice of Impl, because it's the wrong type.
type Abstract interface {
value() string
}
type Impl struct {
str string
}
func (impl Impl) value() string {
return impl.str
}
func ShowAbstracts(arr []Abstract) {
for _, a := range arr {
fmt.Println(a.value())
}
}
func main() {
arr := []Impl{Impl{"a"}, Impl{"b"}}
ShowAbstracts(arr) // This doesn't work, because arr is []Impl, not []Abstract
}
It works with `arr := []Abstract{Impl{"a"}, ...}` of course, but if you already have a 100K slice of Impl, you'd have to copy all 100K elements into a slice of Abstract to call ShowAbstracts in this example.
Slices are backed by arrays, not actual arrays - copying a 100K slice into another slice isn't as intensive as it sounds.
I'm not sure I see this as "interfaces are broken in Go", or something that requires generics to solve? (I do realise that the language itself uses generics to implement the copy keyword for this).
Unfortunately I think copying 100K elements is going to be exactly as painful as it sounds. The `[]Impl` is going to be (more or less) an array of 100K strings. The `[]Abstract` will be an array of interfaces, each of which is internally two pointers, so I'll have to allocate 1.6M of RAM and fill that RAM with pointers in order to convert a `[]Impl` into a `[]Abstract`.
Generics fix this handily, because I can write:
func ShowAbstracts[T Abstract](arr []T) {
for _, a := range arr {
fmt.Println(a.value())
}
}
And now when I try to pass in my `[]Impl`, Go will compile a new `ShowAbstracts(arr []Impl)` transparently behind the scenes.
The problem is that ShowAbstracts could just modify the array and put an instance of Abstract there that _isn't_ an Impl, thus violating the original constraint of arr containing only instances of Impl.
Once again mutability/invariance raises its ugly head!
Duck-typed interfaces are kind of broken in Go, though. I can use an Impl anywhere I can use an Abstract, but if I have a function that takes a slice of Abstract, I can't pass it a slice of Impl, because it's the wrong type.