"XはYを実装していません(...メソッドはポインタレシーバーを持っています)"ということに関してすでにいくつかのQ&Aがありますが、私には、違うことについて話しているようです私の特定の場合.
それで、質問を非常に具体的にする代わりに、私はそれを広く抽象的にしています - このエラーを起こすことができるいくつかの異なるケースがあるようです、誰かがそれを要約してもらえますか?
つまり、問題を回避する方法、そして問題が発生した場合、どのような可能性がありますか。 THX。
このコンパイル時エラーは、インターフェイスタイプにconcreteタイプを割り当てたり、渡したり(または変換)しようとすると発生します。また、タイプ自体はインターフェースを実装せず、タイプへのポインターのみを実装します。
例を見てみましょう:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Stringer
インターフェイスタイプには、String()
という1つのメソッドのみがあります。インターフェイス値Stringer
に保存される値には、このメソッドが必要です。また、MyType
を作成し、pointerレシーバーで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
に割り当てようとしても問題ありません。
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
のメソッドセットには埋め込みMyType
のString()
メソッドが含まれず、*MyType2
のメソッドセットのみが含まれるため、次のようになります。動作します( Go Playground で試してください):
var s Stringer
s = &m2
*MyType
を埋め込み、non-pointerMyType2
のみを使用して動作させることもできます( 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
仕様の関連セクション(セクション 構造タイプ から):
構造型
S
およびT
という名前の型が与えられた場合、昇格されたメソッドは、次のように構造のメソッドセットに含まれます。
S
に匿名フィールドT
が含まれる場合、S
と*S
のメソッドセットには、両方ともレシーバーT
の昇格されたメソッドが含まれます。*S
のメソッドセットには、レシーバー*T
の昇格されたメソッドも含まれます。S
に匿名フィールド*T
が含まれる場合、S
と*S
のメソッドセットには、両方ともレシーバーT
または*T
の昇格されたメソッドが含まれます。
つまり、非ポインター型を埋め込むと、非ポインター埋め込みのメソッドセットは、非埋め込み型レシーバーのメソッドのみを(埋め込み型から)取得します。
ポインター型を埋め込むと、非ポインター埋め込みのメソッドセットは、(埋め込み型から)ポインターと非ポインターの両方のレシーバーを持つメソッドを取得します。
埋め込み型へのポインター値を使用する場合、埋め込み型がポインターであるかどうかに関係なく、埋め込み型へのポインターのメソッドセットは常に(埋め込み型から)ポインターと非ポインターの両方のレシーバーを持つメソッドを取得します。
注:
非常によく似たケースがあります。つまり、MyType
の値をラップするインターフェイス値があり、 type assert 別のインターフェイス値Stringer
をしようとする場合です。この場合、上記の理由によりアサーションは保持されませんが、わずかに異なるランタイムエラーが発生します。
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
ランタイムパニック( Go Playground で試してください):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
型アサートの代わりに変換しようとすると、話しているコンパイル時エラーが発生します。
m := MyType{value: "something"}
fmt.Println(Stringer(m))
短くするために、このコードがあり、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:loadContentの引数にwebLoader(type WebLoader)を引数として使用することはできません:WebLoaderはLoaderを実装していません(Loadメソッドはポインタレシーバを持っています)
だからあなたがする必要があるのはwebLoader := WebLoader{}
を次のように変更することです。
webLoader := &WebLoader{}
それで、なぜあなたはこの関数func (w *WebLoader) Load
をポインタレシーバーを受け入れるために定義するのでそれが修正されるのか。もっと詳しい説明は@iczaと@karoraの回答を読んでください
このようなことが起こっているのを見たときのもう1つのケースは、あるメソッドが内部値を変更し、他のメソッドが変更しないようなインターフェースを作成したい場合です。
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()
}
それから私は前述の "XはYを実装していません(Zメソッドはポインターレシーバーを持っています)"というエラーにはなりませんが、私は悪い日を過ごすでしょう。私のテストが失敗した理由を正確に追いかけています...
代わりに、次のようにポインタを使って型チェックを確実に行う必要があります。
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)
}