是指向接口的指针,而不是接口”混淆。

huangapple go评论106阅读模式
英文:

"<type> is pointer to interface, not interface" confusion

问题

我有一个问题,对我来说似乎有点奇怪。看一下这段代码片段:

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) // <--- 引发异常的地方
}

运行时不接受上述行,因为:

"cannot use fieldfilter (type *coreinterfaces.FieldFilter) as type
*coreinterfaces.FilterInterface in argument to fieldint.AddFilter:
*coreinterfaces.FilterInterface is pointer to interface, not interface"

然而,当将代码更改为:

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并为该接口分配实现。一定有一种优雅的方法来做到这一点。

英文:

I have this problem which seems a bit weird to me. Take a look at this snippet of code:

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
}

On some other package, I have the following code:

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

The run-time won't accept the line mentioned because

> "cannot use fieldfilter (type *coreinterfaces.FieldFilter) as type
> *coreinterfaces.FilterInterface in argument to fieldint.AddFilter:
> *coreinterfaces.FilterInterface is pointer to interface, not interface"

However, when changing the code to:

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

Everything is alright and when debugging the application it really seems to include

I'm a bit confused on this topic. When looking at other blog posts and stack overflow threads discussing this exact same issue (for example - This, or
This) the first snippet which raises this exception should work, because both fieldfilter and fieldmap are initialized as pointers to interfaces, rather than value of interfaces. I haven't been able to wrap my head around what actually happens here that I need to change in order for me not to declare a FieldInterface and assign the implementation for that interface. There must be an elegant way to do this.

答案1

得分: 285

所以你在这里混淆了两个概念。指向结构体的指针和指向接口的指针是不同的。接口可以直接存储结构体,也可以存储指向结构体的指针。在后一种情况下,你仍然直接使用接口,而不是接口的指针。例如:

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] &{}```

在这两种情况下,`DoFoo` 函数中的 `f` 变量只是一个接口,而不是接口的指针。然而,当存储 `f2` 时,接口会持有一个指向 `Foo` 结构体的指针。

几乎从来不会用到接口的指针。事实上,几个版本前,Go 运行时特意更改了接口指针的自动解引用行为(与结构体指针不同),以阻止它们的使用。在绝大多数情况下,接口的指针表示对接口工作原理的误解。

然而,接口有一个限制。如果你直接将一个结构体传递给接口,只有该类型的值方法(例如 `func (f Foo) Dummy()`,而不是 `func (f *Foo) Dummy()`)可以用来满足接口。这是因为你在接口中存储了原始结构体的副本,所以指针方法会产生意外的效果(例如无法修改原始结构体)。因此,默认的经验法则是**在接口中存储结构体的指针**,除非有充分的理由不这样做。

针对你的代码,如果你将 `AddFilter` 函数的签名更改为:

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

并将 GetFilterByID 的签名更改为:

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

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

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

英文:

So you're confusing two concepts here. A pointer to a struct and a pointer to an interface are not the same. An interface can store either a struct directly or a pointer to a struct. In the latter case, you still just use the interface directly, not a pointer to the interface. For example:

type Fooer interface {
	Dummy()
}

type Foo struct{}

func (f Foo) Dummy() {}

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

	DoFoo(f1)
	DoFoo(f2)
}

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

Output:

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

https://play.golang.org/p/I7H_pv5H3Xl

In both cases, the f variable in DoFoo is just an interface, not a pointer to an interface. However, when storing f2, the interface holds a pointer to a Foo structure.

Pointers to interfaces are almost never useful. In fact, the Go runtime was specifically changed a few versions back to no longer automatically dereference interface pointers (like it does for structure pointers), to discourage their use. In the overwhelming majority of cases, a pointer to an interface reflects a misunderstanding of how interfaces are supposed to work.

However, there is a limitation on interfaces. If you pass a structure directly into an interface, only value methods of that type (ie. func (f Foo) Dummy(), not func (f *Foo) Dummy()) can be used to fulfill the interface. This is because you're storing a copy of the original structure in the interface, so pointer methods would have unexpected effects (ie. unable to alter the original structure). Thus the default rule of thumb is to store pointers to structures in interfaces, unless there's a compelling reason not to.

Specifically with your code, if you change the AddFilter function signature to:

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

And the GetFilterByID signature to:

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

Your code will work as expected. fieldfilter is of type *FieldFilter, which fullfills the FilterInterface interface type, and thus AddFilter will accept it.

Here's a couple of good references for understanding how methods, types, and interfaces work and integrate with each other in Go:

答案2

得分: 20

获取指定ID的过滤器 *FilterInterface

当我遇到这个错误时,通常是因为我指定了一个指向接口的指针,而不是一个实际上将是指向实现该接口的结构体的指针。

*interface{...} 有一个有效的用途,但更常见的情况是我只是在思考“这是一个指针”,而不是“这是一个接口,在我编写的代码中恰好是一个指针”。

英文:
GetFilterByID(i uuid.UUID) *FilterInterface

When I get this error, it's usually because I'm specifying a pointer to an interface instead of an interface ( that will actually be a pointer to my struct that fulfills the interface ).

There's a valid use for *interface{...} but more commonly I just am thinking 'this is a pointer' instead of 'this is an interface which happens to be a pointer in the code I'm writing'

huangapple
  • 本文由 发表于 2017年6月5日 21:37:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/44370277.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定