[pointers] “<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
}

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

“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)
}

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

이 주제에 대해 약간 혼란 스럽습니다. 이 동일한 문제를 논의 다른 블로그 게시물 및 스택 오버플로 스레드를 볼 때 (예 – , 또는
) fieldmap fieldfilter 모두가 인터페이스 포인터로 초기화되기 때문에 오히려 값보다,이 예외가 작동합니다 제기 최초의 조각 인터페이스. FieldInterface를 선언하지 않고 해당 인터페이스에 대한 구현을 할당하지 않도록 변경해야하는 여기서 실제로 발생하는 일에 대해 머리를 감쌀 수 없었습니다. 이를위한 우아한 방법이 있어야합니다.



답변

그래서 여기서 두 가지 개념을 혼동하고 있습니다. 구조체에 대한 포인터와 인터페이스에 대한 포인터는 동일하지 않습니다. 인터페이스는 직접 구조체 중 하나를 저장할 수 있습니다 또는 구조체에 대한 포인터를. 후자의 경우에도 인터페이스에 대한 포인터가 아니라 인터페이스를 직접 사용합니다 . 예를 들면 :

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

두 경우 모두, f변수의 DoFoo단지 인터페이스입니다 하지 인터페이스에 대한 포인터. 기억 그러나 f2, 인터페이스는 유지 (A)에 대한 포인터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유형 인 *FieldFilterfullfills, FilterInterface따라서 인터페이스 유형을, 그리고 AddFilter그것을 받아 들일 것입니다.

다음은 Go에서 메서드, 유형 및 인터페이스가 작동하고 서로 통합되는 방식을 이해하기위한 몇 가지 좋은 참고 자료입니다.


답변

GetFilterByID(i uuid.UUID) *FilterInterface

이 오류가 발생하면 일반적으로 인터페이스 대신 인터페이스에 대한 포인터를 지정하기 때문입니다 (실제로는 인터페이스를 수행하는 내 구조체에 대한 포인터가됩니다).

* interface {…}에 대한 유효한 사용이 있지만 더 일반적으로 ‘이것은 내가 작성중인 코드의 포인터가되는 인터페이스입니다’대신 ‘이것은 포인터입니다’라고 생각합니다.

받아 들여진 답변이 상세하지만 문제 해결에 도움이되지 않았기 때문에 그냥 던졌습니다.


답변