Development Tip

yourdevel 2020. 11. 24. 19:59
반응형

인터페이스가 아니라 인터페이스에 대한 포인터”혼동


동료 개발자 여러분,

나에게 조금 이상해 보이는 문제가 있습니다. 이 코드 스 니펫을 살펴보십시오.

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
}

런타임은 언급 된 줄을 받아들이지 않을 것입니다.

"fieldint.AddFilter에 대한 인수에서 * coreinterfaces.FilterInterface 유형으로 fieldfilter (유형 * coreinterfaces.FieldFilter)를 사용할 수 없습니다. * coreinterfaces.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)
}

모든 것이 정상이며 응용 프로그램을 디버깅 할 때 실제로 포함되는 것 같습니다.

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.


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 {
    Foo()
}

type Foo struct{}

func (f Foo) Foo() {}

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

    DoFoo(f1)
    DoFoo(f2)
}

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

Output:

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

https://play.golang.org/p/BGV9d1-IRW

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) Foo(), not func (f *Foo) Foo()) 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:


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'

Just throwing it out there because the accepted answer, though detailed, didn't help me troubleshoot.

참고URL : https://stackoverflow.com/questions/44370277/type-is-pointer-to-interface-not-interface-confusion

반응형