“ < type > 是指向接口的指针,不是接口”混淆

我遇到了这个问题,对我来说有点奇怪:

package coreinterfaces


type FilterInterface interface {
Filter(s *string) bool
}


type FieldFilter struct {
Key string
Val string
}


func (ff *FieldFilter) Filter(s *string) bool {
// Some code
}


type FilterMapInterface interface {
AddFilter(f *FilterInterface) uuid.UUID
RemoveFilter(i uuid.UUID)
GetFilterByID(i uuid.UUID) *FilterInterface
}


type FilterMap struct {
mutex   sync.Mutex
Filters map[uuid.UUID]FilterInterface
}


func (fp *FilterMap) AddFilter(f *FilterInterface) uuid.UUID {
// Some code
}


func (fp *FilterMap) RemoveFilter(i uuid.UUID) {
// Some code
}


func (fp *FilterMap) GetFilterByID(i uuid.UUID) *FilterInterface {
// Some code
}

在其他软件包中,我有以下代码:

func DoFilter() {
fieldfilter := &coreinterfaces.FieldFilter{Key: "app", Val: "152511"}
filtermap := &coreinterfaces.FilterMap{}
_ = filtermap.AddFilter(fieldfilter) // <--- Exception is raised here
}

运行时不会接受提到的行,因为

”不能使用 FieldFilter (type * coreinterface. FieldFilter)作为类型 * coreinterface. FilterInterface 参数为 field.AddFilter: * 核心接口。 FilterInterface 是指向接口的指针,而不是接口”

不过,在将代码更改为:

func DoBid() error {
bs := string(b)
var ifilterfield coreinterfaces.FilterInterface
fieldfilter := &coreinterfaces.FieldFilter{Key: "app", Val: "152511"}
ifilterfield = fieldfilter
filtermap := &coreinterfaces.FilterMap{}
_ = filtermap.AddFilter(&ifilterfield)
}

一切正常,当调试应用程序时,它似乎真的包括

我对这个话题有点困惑。当查看其他博客文章和堆栈溢出线程讨论完全相同的问题(例如 -这个,或 引发此异常的第一个代码片段应该可以工作,因为 fieldfilter 和 fieldmap 都被初始化为指向接口的指针,而不是接口的值。我还不能理解这里实际发生了什么,为了不声明 FieldInterface 并为该接口分配实现,我需要对其进行更改。一定有什么优雅的方法可以做到这一点。

95908 次浏览

所以你把两个概念搞混了。指向结构的指针和指向接口的指针是不同的。一个接口可以直接存储一个结构体 或者一个指向结构体的指针。在后一种情况下,您仍然只是直接使用接口,没有是指向接口的指针。例如:

type Fooer interface {
Dummy()
}


type Foo struct{}


func (f Foo) Dummy() {}


func main() {
var f1 Foo
var f2 *Foo = &Foo{}


DoFoo(f1)
DoFoo(f2)
}


func DoFoo(f Fooer) {
fmt.Printf("[%T] %+v\n", f, f)
}

产出:

[main.Foo] {}
[*main.Foo] &{}

Https://play.golang.org/p/i7h_pv5h3xl

在这两种情况下,DoFoo中的 f变量只是一个接口,没有是一个指向接口的指针。但是,当存储 f2时,接口 持有是一个指向 Foo结构的指针。

指向接口的指针几乎都是有用的 永远不会。事实上,Go 运行时被特别修改了几个版本,不再自动取消引用接口指针(就像对结构指针那样) ,以阻止它们的使用。在绝大多数情况下,指向接口的指针反映了对接口应该如何工作的误解。

但是,接口有一个限制。如果将结构直接传递到接口,则只能传递该类型的 价值方法(即。func (f Foo) Dummy(),而不是 func (f *Foo) Dummy())可以用来实现接口。这是因为您在接口中存储了原始结构的副本,所以指针方法会产生意想不到的效果(即。不能改变原来的结构)。因此,默认的经验法则是 在接口中存储指向结构的指针,除非有一个令人信服的理由不这样做。

如果将 AddFilter 函数签名更改为:

func (fp *FilterMap) AddFilter(f FilterInterface) uuid.UUID

以及 GetFilterByID 签名:

func (fp *FilterMap) GetFilterByID(i uuid.UUID) FilterInterface

您的代码将按预期工作。fieldfilter*FieldFilter类型的,它满足 FilterInterface接口类型,因此 AddFilter将接受它。

下面是一些很好的参考资料,可以帮助你理解 Go 中的方法、类型和接口是如何工作和相互集成的:

GetFilterByID(i uuid.UUID) *FilterInterface

当我得到这个错误时,通常是因为我指定了一个指向接口的指针,而不是指向接口(这实际上是指向满足接口的结构的指针)。

接口{ ... }有一个有效的用法,但更常见的是,我认为“这是一个指针”,而不是“这是一个接口,碰巧是我正在写的代码中的一个指针”