この「X は Y を実装していません (... メソッドにはポインター レシーバーがあります)」という件については、すでにいくつかの Q&A がありますが、私にとっては、それらは異なる事柄について話しているようで、私の特定のケースには当てはまらないようです。
したがって、質問を非常に具体的にするのではなく、広く抽象的なものにします。このエラーが発生する可能性があるケースはいくつかあるようですが、誰か要約してもらえますか?
つまり、問題を回避する方法と、問題が発生した場合の可能性があるものは何ですか? ありがとうございます。
ベストアンサー1
このコンパイル時エラーは、具体的な型をインターフェース型に割り当てたり、渡したり (または変換したり) しようとしたときに発生します。型自体はインターフェースを実装しておらず、型へのポインターのみを実装しています。
要約:割り当てインターフェース型の変数への代入は、代入される値が代入先のインターフェースを実装している場合に有効です。メソッドセットは、インターフェースのスーパーセットです。ポインター型のメソッド セットには、ポインター レシーバーと非ポインター レシーバーの両方を持つメソッドが含まれます。非ポインター型のメソッド セットには、非ポインター レシーバーを持つメソッドのみが含まれます。
例を見てみましょう:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
インターフェースStringer
型には、メソッドが1つだけあります: String()
。インターフェース値に格納される値はすべて、Stringer
このメソッドを持つ必要があります。また、 を作成し、ポインタレシーバを持つMyType
メソッドを作成しました。これは、メソッドが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)
そして期待通りの結果が得られた(遊び場に行く):
something
したがって、このコンパイル時エラーが発生する要件は次のとおりです。
- 割り当てられる(または渡される、または変換される)非ポインタ具体的な型の値
- 割り当てられる(または渡される、または変換される)インターフェース型
- 具体的な型にはインターフェースの必要なメソッドがありますが、ポインタレシーバーが付いています
問題を解決する可能性:
- 値へのポインタを使用する必要があります。そのメソッドセットには、ポインタレシーバを持つメソッドが含まれます。
- または、レシーバー型を非ポインターに変更する必要があります。これにより、非ポインターの具象型のメソッド セットにもメソッドが含まれるようになります (したがって、インターフェイスが満たされます)。メソッドが値を変更する必要がある場合、非ポインター レシーバーはオプションではないため、これが実行可能であるかどうかはわかりません。
構造と埋め込み
使用する場合構造体と埋め込み、多くの場合、インターフェースを実装する(メソッド実装を提供する)のは「あなた」ではなく、 に埋め込む型ですstruct
。次の例のように:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
再び、コンパイル時エラーが発生します。 のメソッドセットには埋め込まれた のメソッドMyType2
が含まれておらず、 のメソッドセットのみが含まれているため、次のコードは機能します(String()
MyType
*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
仕様書の関連セクション(セクション構造体型):
構造体型
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))