ChatGPT解决这个技术问题 Extra ChatGPT

X does not implement Y (... method has a pointer receiver)

There are already several Q&As on this "X does not implement Y (... method has a pointer receiver)" thing, but to me, they seems to be talking about different things, and not applying to my specific case.

So, instead of making the question very specific, I'm making it broad and abstract -- Seems like there are several different cases that can make this error happen, can someone summary it up please?

I.e., how to avoid the problem, and if it occurs, what are the possibilities? Thx.


i
icza

This compile-time error arises when you try to assign or pass (or convert) a concrete type to an interface type; and the type itself does not implement the interface, only a pointer to the type.

Short summary: An assignment to a variable of interface type is valid if the value being assigned implements the interface it is assigned to. It implements it if its method set is a superset of the interface. The method set of pointer types includes methods with both pointer and non-pointer receiver. The method set of non-pointer types only includes methods with non-pointer receiver.

Let's see an example:

type Stringer interface {
    String() string
}

type MyType struct {
    value string
}

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

The Stringer interface type has one method only: String(). Any value that is stored in an interface value Stringer must have this method. We also created a MyType, and we created a method MyType.String() with pointer receiver. This means the String() method is in the method set of the *MyType type, but not in that of MyType.

When we attempt to assign a value of MyType to a variable of type Stringer, we get the error in question:

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)

But everything is ok if we try to assign a value of type *MyType to Stringer:

s = &m
fmt.Println(s)

And we get the expected outcome (try it on the Go Playground):

something

So the requirements to get this compile-time error:

A value of non-pointer concrete type being assigned (or passed or converted)

An interface type being assigned to (or passed to, or converted to)

The concrete type has the required method of the interface, but with a pointer receiver

Possibilities to resolve the issue:

A pointer to the value must be used, whose method set will include the method with the pointer receiver

Or the receiver type must be changed to non-pointer, so the method set of the non-pointer concrete type will also contain the method (and thus satisfy the interface). This may or may not be viable, as if the method has to modify the value, a non-pointer receiver is not an option.

Structs and embedding

When using structs and embedding, often it's not "you" that implement an interface (provide a method implementation), but a type you embed in your struct. Like in this example:

type MyType2 struct {
    MyType
}

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

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

Again, compile-time error, because the method set of MyType2 does not contain the String() method of the embedded MyType, only the method set of *MyType2, so the following works (try it on the Go Playground):

var s Stringer
s = &m2

We can also make it work, if we embed *MyType and using only a non-pointer MyType2 (try it on the Go Playground):

type MyType2 struct {
    *MyType
}

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

var s Stringer
s = m2

Also, whatever we embed (either MyType or *MyType), if we use a pointer *MyType2, it will always work (try it on the 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))

Thanks for the extremely comprehensive answer. Sorry for responding late as strangely I didn't get the SO notification. One case that I searched, the answer was that the "member functions" should either be all pointer types, e.g., "func (m *MyType)", or none. Is it so? Can I mix different types of "member functions", e.g., func (m *MyType) & func (m MyType)?
@xpt You can mix pointer and non-pointer receivers, it's not a requirement to make all the same. It's just weird if you have 19 methods with pointer receiver and you make one with non-pointer receiver. It also makes it harder to track which methods are part of which types' method sets if you start mixing them. More details in this answer: Value receiver vs. Pointer receiver in Golang?
@JoelEdström Yes, it's possible, but it makes little sense. For example you may type-assert the value of the non-pointer type and store it in a variable, e.g. x := i.(MyType), and then you can call methods with pointer receiver on it, e.g. i.String(), which is a shorthand for (&i).String() which succeeds because variables are addressable. But the pointer method changing the value (the pointed value) will not be reflected in the value wrapped in the interface value, that's why it makes little sense.
Moral of the story: Pointer types and non-pointers types are not the same things even if they use the same underlying type. So, treat them accordingly. That's why which one implements a method to satisfy an interface matters!
@DeepNightTwo Methods of *T are not included in method set of S because S might not be addressable (e.g. function return value or result of map indexing), and also because often only a copy is present / received, and if taking its address is allowed, the method with pointer receiver could only modify the copy (confusion as you would assume the original is modified). See this answer for an example: Using reflection SetString.
S
Saman

To keep it short and simple, let say 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")
}

The above 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)

To fix it you only need to change webLoader := WebLoader{} to following:

webLoader := &WebLoader{} 

Why this will fix the issue? Because you defined this function func (w *WebLoader) Load to accept a pointer receiver. For more explanation please read @icza and @karora answers


By far this was the easiest comment to understand. And directly solved the issue I was facing..
@Maxs728 Agreed, quite uncommon in answers to the many many Go problems.
to the point and easy to understand. What I need in the middle of the deadline
great answer, simplicity is the mother of success
JESUS APPROVES THIS ANSWER ❤️
k
karora

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

鄭元傑

Extend from above answers (Thanks for all of your answers) I think it would be more instinctive to show all the methods of pointer / non pointer struct.

Here is the playground code. https://play.golang.org/p/jkYrqF4KyIf

To summarize all the example.

Pointer struct type would include all non pointer / pointer receiver methods Non pointer struct type would only include non pointer receiver methods.

For embedded struct

non pointer outer struct + non pointer embedded struct => only non pointer receiver methods. non pointer outer struct + pointer embedded struct / pointer outer struct + non pointer embedded struct / pointer outer struct + pointer embedded struct => all embedded methods


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now