原文https://stackoverflow.com/questions/40823315/x-does-not-implement-y-method-has-a-pointer-receiver
正文
当你尝试将一个确切类型分配或转换接口类型时,在编译时会报错
也就是X does not implement Y (method has a pointer receiver)的由来
提示类型本身不实现接口,只能是类型的指针
让我们看一个例子
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Stringer接口只有一个方法,任何存储在Stringer接口的值都必须实现这个方法,我们创建一个MyType.String()方法是一个指针接收者,这意味着String方法在*MyType的方法集中,而非MyType的方法集中
当我们试图将MyType变量分配给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,一切都是ok的
s = &m
fmt.Println(s)
我们得到了期望的输出
something
所以我们得到这个编译的错误需要以下条件
1.分配一个非指针的确切类型的值(或传递以及转换)
2.一个接口类型将要被分配(或传递或转换)
3.一个确切的实现了接口的这些方法,但是是指针接收者
以下方案可以解决这个问题
1.使用指针类型,指针类型的方法集将包含指针接收者的方法
2.或将函数的指针接收者改为非指针接收者,因此非指针接收者的方法集也将包含该方法,这可能可以解决问题,也可能出现新的问题,就比如方法会修改值,而非指针接收者无法做到/
结构和嵌入
使用结构和嵌入的时候,实现接口的通常不是你,而是嵌入到你的类型,就像下面这个例子
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
又一次出现同样的编译错误,因为方法集Mytype2嵌入的MyType不包含String(),仅有*MyType2具有这个方法集,所以接下来我们可以
var s Stringer
s = &m2
我们可以让他工作,如果我们嵌入一个*MyType和使用非指针Mytype2
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
无论我们嵌入(MyType或*MyType),如何我们使用指针MyType2她都可以正常工作
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
规范中的相关部分https://golang.org/ref/spec#Struct_types
给定一个类型S与嵌入类型T,提升的方法包含在结构的方法集的规则如下所示
如果S包含匿名字段T,则方法集S和指针S都包含T的接受者提升方法,*S还包含 指针T的接收者提升方法
如果S包含匿名字段指针T,则方法集S以及 指针S都包括T和指针T的提升方法
换句话说,如果我们嵌入了一个非指针类型,那么嵌入该非指针嵌入的结构的方法集只能得到非指针接收者的方法(来自嵌入类型)
如果我们嵌入了一个指针类型,嵌入该指针的结构的方法集将同时获得指针和非指针接收器的方法(来自嵌入类型)
如果我们使用嵌入结构的结构的指针值,无论嵌入类型是否是指针,该指针的方法集总是获得指针的和非指针接收者的方法(来自嵌入类型)
提示:
有一个非常近似的情况,当你有一个接口类型包裹着MyType值,和你尝试将他断言另一个接口值Stringer,在这个例子中断言不成立,来自上诉原因,但是我们会得到一个完全不一样的运行时错误
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
运行错误
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
尝试使用转换而不是类型断言,我们得到了我们正在讨论的编译时错误
m := MyType{value: "something"}
fmt.Println(Stringer(m))