已经有几个Q& As就这个“ X没有实现Y(...方法有一个指针接收器)”的东西,但对我来说,他们似乎在谈论不同的事情,而不适用于我的具体案例。
因此,我没有将问题变得非常具体,而是将其广泛和抽象化 - 似乎有几个不同的案例可以使这个错误发生,有人可以总结一下吗?
即,如何避免这个问题,如果发生,有什么可能性? THX。
答案 0 :(得分:239)
当您尝试将具体类型分配或传递(或转换)为接口类型时,会出现此编译时错误;并且类型本身不实现接口,只有指向类型的指针。
让我们看一个例子:
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()
方法位于*MyType
类型的method set,但不属于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
,那么一切正常:
s = &m
fmt.Println(s)
我们得到了预期的结果(在Go Playground上试试):
something
因此需要获得此编译时错误:
解决问题的可能性:
使用structs and embedding时,通常不会"你"实现接口(提供方法实现),但是您嵌入struct
的类型。就像在这个例子中一样:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
同样,编译时错误,因为MyType2
的方法集不包含嵌入式String()
的{{1}}方法,只包含MyType
的方法集,所以以下工作(在Go Playground上尝试):
*MyType2
如果我们嵌入var s Stringer
s = &m2
并仅使用非指针 *MyType
(在Go Playground上尝试),我们也可以使其有效:< / p>
MyType2
此外,无论我们嵌入了什么(type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
或MyType
),如果我们使用指针*MyType
,它将始终有效(在Go Playground上尝试):
*MyType2
规范中的相关部分(来自Struct types部分):
给定结构类型
type MyType2 struct { *MyType } m := MyType{value: "something"} m2 := MyType2{MyType: &m} var s Stringer s = &m2
和名为S
的类型,提升的方法包含在结构的方法集中,如下所示:
- 如果
T
包含匿名字段S
,则T
和S
的方法集都包含带有接收方*S
的提升方法。方法集T
还包括使用接收方*S
的推广方法。- 如果
*T
包含匿名字段S
,则*T
和S
的方法集都包含带有接收方*S
或T
的提升方法}。
换句话说:如果我们嵌入一个非指针类型,非指针嵌入器的方法集只能获得带有非指针接收器的方法(来自嵌入式类型)。
如果嵌入指针类型,非指针嵌入器的方法集将获得带有指针和非指针接收器的方法(来自嵌入式类型)。
如果我们对嵌入器使用指针值,无论嵌入类型是否为指针,指向嵌入器的指针的方法集总是获得带有指针和非指针接收器的方法(来自嵌入式类型) )。
注意:强>
有一个非常类似的情况,即当你有一个包含*T
值的接口值,并尝试type assert来自它的另一个接口值MyType
时。在这种情况下,由于上述原因断言不会成立,但我们得到的运行时错误略有不同:
Stringer
运行时恐慌(在Go Playground上尝试):
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
尝试转换而不是类型断言,我们得到了我们正在讨论的编译时错误:
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
答案 1 :(得分:12)
为了简短起见,假设您拥有此代码,并且具有Loader接口和实现此接口的WebLoader。
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")
}
所以这段代码会给你这个编译时错误
./ main.go:20:13:无法将webLoader(类型WebLoader)用作类型Loader 在loadContent的参数中: WebLoader未实现Loader(Load方法具有指针接收器)
因此,您唯一需要做的就是将webLoader := WebLoader{}
更改为以下内容:
webLoader := &WebLoader{}
所以为什么会解决,因为您定义了此函数func (w *WebLoader) Load
来接受指针接收器。有关更多说明,请阅读@icza和@karora答案
答案 2 :(得分:3)
当我看到发生这种事情的另一种情况是,如果我想创建一个接口,其中某些方法将修改内部值而其他方法则不会。
type GetterSetter interface {
GetVal() int
SetVal(x int) int
}
然后实现此接口的东西可能是:
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
}
所以实现类型可能有一些方法是指针接收器,有些方法不是,因为我有各种各样的GetterSetters这些东西,我想在我的测试中检查它们都是做到了预期。
如果我要做这样的事情:
myTypeInstance := MyType{ 7 }
... maybe some code doing other stuff ...
var f interface{} = myTypeInstance
_, ok := f.(GetterSetter)
if !ok {
t.Fail()
}
然后我没有得到上述&#34; X没有实现Y(Z方法有指针接收器)&#34;错误(因为它是一个编译时错误),但我将有一个糟糕的一天追逐我的测试失败的确切原因......
相反,我必须确保使用指针进行类型检查,例如:
var f interface{} = new(&MyTypeA)
...
或者:
myTypeInstance := MyType{ 7 }
var f interface{} = &myTypeInstance
...
然后所有人都对测试感到满意!
但是等等!在我的代码中,也许我有接受GetterSetter的方法:
func SomeStuff(g GetterSetter, x int) int {
if x > 10 {
return g.GetVal() + 1
}
return g.GetVal()
}
如果我从另一个类型方法中调用这些方法,则会产生错误:
func (m MyTypeA) OtherThing(x int) {
SomeStuff(m, x)
}
以下任一调用都可以使用:
func (m *MyTypeA) OtherThing(x int) {
SomeStuff(m, x)
}
func (m MyTypeA) OtherThing(x int) {
SomeStuff(&m, x)
}
答案 3 :(得分:1)
从以上答案延伸(感谢您的所有回答)
我认为展示指针/非指针结构的所有方法会更本能。
这是游乐场代码。 https://play.golang.org/p/jkYrqF4KyIf
总结所有的例子。
对于嵌入式结构