development

X는 Y를 구현하지 않습니다 (… 메소드에는 포인터 수신기가 있습니다)

big-blog 2020. 6. 14. 09:38
반응형

X는 Y를 구현하지 않습니다 (… 메소드에는 포인터 수신기가 있습니다)


이 " X는 Y를 구현하지 않습니다 (... 메소드에는 포인터 수신기가 있습니다 "라는 것에 대한 몇 가지 Q & A 있지만, 저에게는 다른 것들에 대해 이야기하고 있으며 특정 사례에 적용하지 않는 것 같습니다.

따라서 질문을 매우 구체적으로 만드는 대신 광범위하고 추상적으로 만들고 있습니다.이 오류가 발생할 수있는 몇 가지 다른 사례가있는 것처럼 보입니다. 누군가 그것을 요약 해 주시겠습니까?

즉, 문제를 피하는 방법과 문제가 발생하면 어떤 가능성이 있습니까? 고마워.


이 컴파일 타임 오류는 구체적 유형을 인터페이스 유형 에 지정하거나 전달 (또는 변환)하려고 할 때 발생 합니다. 타입 자체는 인터페이스를 구현하지 않고 type에 대한 포인터 만 구현합니다 .

예를 보자.

type Stringer interface {
    String() string
}

type MyType struct {
    value string
}

func (m *MyType) String() string { return m.value }

Stringer인터페이스 유형은 하나의 방법 만이 있습니다 String(). 인터페이스 값에 저장된 모든 값 Stringer에는이 방법이 있어야합니다. 또한을 만들고 포인터 수신기를 사용 MyType하여 메서드 MyType.String()만들었습니다 . 이는 메소드가 유형 메소드 세트있지만 메소드 세트 에는 없음을 의미 합니다.String()*MyTypeMyType

MyTypetype의 변수에 값을 할당하려고 Stringer하면 해당 오류가 발생합니다.

m := MyType{value: "something"}

var s Stringer
s = m // cannot use m (type MyType) as type Stringer in assignment:
      //   MyType does not implement Stringer (String method has pointer receiver)

우리는 유형의 값을 할당하려고한다면 모든 것이 괜찮 *MyType로를 Stringer:

s = &m
fmt.Println(s)

그리고 우리는 예상 결과를 얻습니다 ( Go Playground 에서 시도하십시오 ).

something

따라서이 컴파일 타임 오류가 발생하기위한 요구 사항 :

  • 포인터아닌 콘크리트 유형 의 값 이 지정되거나 전달되거나 변환됩니다.
  • 인터페이스 유형에 지정되거나 전달되거나 변환되는 인터페이스 유형
  • 구체적 유형에는 필요한 인터페이스 방법이 있지만 포인터 수신기가 있습니다.

문제를 해결할 수있는 가능성 :

  • 값에 대한 포인터를 사용해야하며, 이의 메소드 세트에는 포인터 수신자가있는 메소드가 포함됩니다.
  • 또는 리시버 유형을 non-pointer 로 변경해야 하므로 비 포인터 콘크리트 유형의 메소드 세트도 메소드를 포함하므로 인터페이스를 만족시킵니다. 메소드가 값을 수정해야하는 것처럼 비 포인터 리시버는 옵션이 아닌 것처럼 이는 실행 가능하거나 불가능할 수 있습니다.

구조 및 임베딩

구조체와 embedding을 사용할 때 , 종종 인터페이스를 구현하는 것은 "사용자"가 아니라 (메소드 구현을 제공),에 포함 된 타입 struct입니다. 이 예에서와 같이 :

type MyType2 struct {
    MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: m}

var s Stringer
s = m2 // Compile-time error again

다시 한 번, 컴파일 시간 오류는의 메소드 세트에 embed MyType2String()메소드를 포함하지 않기 때문에의 메소드 세트 MyType포함 *MyType2하므로 다음 작업이 수행됩니다 ( Go Playground 에서 시도 ).

var s Stringer
s = &m2

포인터*MyType아닌 포인터 포함 하고 사용하는 경우 MyType2에도 Go Playground 에서 시도해보십시오 .

type MyType2 struct {
    *MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: &m}

var s Stringer
s = m2

또한 우리가 포함하거나 ( MyType또는 *MyType) 포인터를 사용 *MyType2하면 항상 작동합니다 ( Go Playground 에서 시도하십시오 ).

type MyType2 struct {
    *MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: &m}

var s Stringer
s = &m2

Relevant section from the spec (from section Struct types):

Given a struct type S and a type named T, promoted methods are included in the method set of the struct as follows:

  • If S contains an anonymous field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.
  • If S contains an anonymous field *T, the method sets of S and *S both include promoted methods with receiver T or *T.

So in other words: if we embed a non-pointer type, the method set of the non-pointer embedder only gets the methods with non-pointer receivers (from the embedded type).

If we embed a pointer type, the method set of the non-pointer embedder gets methods with both pointer and non-pointer receivers (from the embedded type).

If we use a pointer value to the embedder, regardless of whether the embedded type is pointer or not, the method set of the pointer to the embedder always gets methods with both the pointer and non-pointer receivers (from the embedded type).

Note:

There is a very similar case, namely when you have an interface value which wraps a value of MyType, and you try to type assert another interface value from it, Stringer. In this case the assertion will not hold for the reasons described above, but we get a slightly different runtime-error:

m := MyType{value: "something"}

var i interface{} = m
fmt.Println(i.(Stringer))

Runtime panic (try it on the Go Playground):

panic: interface conversion: main.MyType is not main.Stringer:
    missing method String

Attempting to convert instead of type assert, we get the compile-time error we're talking about:

m := MyType{value: "something"}

fmt.Println(Stringer(m))

To keep it short, let say you have this code and you have a Loader interface and a WebLoader that implements this interface.

package main

import "fmt"

// Loader defines a content loader
type Loader interface {
    Load(src string) string
}

// WebLoader is a web content loader
type WebLoader struct{}

// Load loads the content of a page
func (w *WebLoader) Load(src string) string {
    return fmt.Sprintf("I loaded this page %s", src)
}

func main() {
    webLoader := WebLoader{}
    loadContent(webLoader)
}

func loadContent(loader Loader) {
    loader.Load("google.com")
}

So this code will give you this compile time error

./main.go:20:13: cannot use webLoader (type WebLoader) as type Loader in argument to loadContent: WebLoader does not implement Loader (Load method has pointer receiver)

So what you only need to do is to change webLoader := WebLoader{} to following:

webLoader := &WebLoader{} 

So why it will fix because you define this function func (w *WebLoader) Load to accept a pointer receiver. For more explanation please read @icza and @karora answers


Another case when I have seen this kind of thing happening is if I want to create an interface where some methods will modify an internal value and others will not.

type GetterSetter interface {
   GetVal() int
   SetVal(x int) int
}

Something that then implements this interface could be like:

type MyTypeA struct {
   a int
}

func (m MyTypeA) GetVal() int {
   return a
}

func (m *MyTypeA) SetVal(newVal int) int {
    int oldVal = m.a
    m.a = newVal
    return oldVal
}

So the implementing type will likely have some methods which are pointer receivers and some which are not and since I have quite a variety of these various things that are GetterSetters I'd like to check in my tests that they are all doing the expected.

If I were to do something like this:

myTypeInstance := MyType{ 7 }
... maybe some code doing other stuff ...
var f interface{} = myTypeInstance
_, ok := f.(GetterSetter)
if !ok {
    t.Fail()
}

Then I won't get the aforementioned "X does not implement Y (Z method has pointer receiver)" error (since it is a compile-time error) but I will have a bad day chasing down exactly why my test is failing...

Instead I have to make sure I do the type check using a pointer, such as:

var f interface{} = new(&MyTypeA)
 ...

Or:

myTypeInstance := MyType{ 7 }
var f interface{} = &myTypeInstance
...

Then all is happy with the tests!

But wait! In my code, perhaps I have methods which accept a GetterSetter somewhere:

func SomeStuff(g GetterSetter, x int) int {
    if x > 10 {
        return g.GetVal() + 1
    }
    return g.GetVal()
}

If I call these methods from inside another type method, this will generate the error:

func (m MyTypeA) OtherThing(x int) {
    SomeStuff(m, x)
}

Either of the following calls will work:

func (m *MyTypeA) OtherThing(x int) {
    SomeStuff(m, x)
}

func (m MyTypeA) OtherThing(x int) {
    SomeStuff(&m, x)
}

참고URL : https://stackoverflow.com/questions/40823315/x-does-not-implement-y-method-has-a-pointer-receiver

반응형