当我们在 Go 中定义接口时,最好在辅助函数中使用该接口,这样我们就可以通用地处理该接口。这就像 Go 中的魅力一样。但是当我们有一片实现接口的结构时,使用起来似乎非常麻烦。
公开接口的示例包:
package tryshit
import "fmt"
type HelloAble interface {
Hello()
}
type Foo struct {
Name string
}
func (receiver *Foo) Hello() {
fmt.Println("Hello: " + receiver.Name)
}
type Bar struct {
Alias string
}
func (receiver *Bar) Hello() {
fmt.Println("Hello: " + receiver.Alias)
}
func ConsumeInterface(helloAble HelloAble) {
helloAble.Hello()
}
func ConsumeInterfaces[T HelloAble](helloAbles []T) {
for _, h := range helloAbles {
h.Hello()
}
}
我们现在可以这样运行:
package main
import (
"tryShit/pkg/tryshit"
)
func main() {
foo := &tryshit.Foo{
Name: "foo",
}
bar := &tryshit.Bar{
Alias: "bar",
}
// Works straight forward and like a charm
tryshit.ConsumeInterface(foo)
tryshit.ConsumeInterface(bar)
// This on the other hand requires `foos` to be a pointer slice
// otherwise calling `tryshit.ConsumeInterfaces` won't work.
// Or we need to set the receiver on Foo to a value instead of a pointer
foos := []*tryshit.Foo{
{Name: "Foo1"},
{Name: "Foo2"},
{Name: "Foo3"},
}
tryshit.ConsumeInterfaces(foos)
}
https://go.dev/play/p/vmQSc3w4qsT
我的问题是我有一个已经定义且无法更改的结构。我收到的切片是带有值的切片,而不是带有指针的切片。为了使这段代码工作,我必须做类似的事情:
var foosPointerSlice []*HelloAble
copier.Copy(&b.foos, &foosPointerSlice)
tryshit.ConsumeInterfaces(foosPointerSlice)
我在想:如果这是解决这个问题的唯一方法,为什么还要编写一个辅助函数,同时在每个需要辅助函数的地方循环,就像使用辅助函数一样麻烦:
for _, foo := range foos {
tryshit.ConsumeInterface(&foo)
}
有没有办法处理这个惯用且干净的?
您需要使指针接收器成为显式约束:
type HelloAblePtr[T any] interface {
HelloAble
*T
}
*Foo
满足HelloAblePtr[Foo]
并且*Bar
满足HelloAblePtr[Bar]
,所以你可以将ConsumeInterfaces
定义为:
func ConsumeInterfaces[T any, HA HelloAblePtr[T]](helloAbles []T) {
for _, h := range helloAbles {
(HA)(&h).Hello()
}
}