X は Y を実装していません (... メソッドにはポインター レシーバーがあります) 質問する

X は Y を実装していません (... メソッドにはポインター レシーバーがあります) 質問する

この「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*ST*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))

おすすめ記事