1. ホーム
  2. pointers

[解決済み] 「<type>はインターフェースへのポインターであり、インターフェースではない」混乱

2022-04-27 06:27:06

質問

ちょっと変な問題があるんです。このコードのスニペットを見てください。

package coreinterfaces

type FilterInterface interface {
    Filter(s *string) bool
}

type FieldFilter struct {
    Key string
    Val string
}

func (ff *FieldFilter) Filter(s *string) bool {
    // Some code
}

type FilterMapInterface interface {
    AddFilter(f *FilterInterface) uuid.UUID     
    RemoveFilter(i uuid.UUID)                   
    GetFilterByID(i uuid.UUID) *FilterInterface
}

type FilterMap struct {
    mutex   sync.Mutex
    Filters map[uuid.UUID]FilterInterface
}

func (fp *FilterMap) AddFilter(f *FilterInterface) uuid.UUID {
    // Some code
}

func (fp *FilterMap) RemoveFilter(i uuid.UUID) {
    // Some code
}

func (fp *FilterMap) GetFilterByID(i uuid.UUID) *FilterInterface {
    // Some code
}

別のパッケージでは、次のようなコードになっています。

func DoFilter() {
    fieldfilter := &coreinterfaces.FieldFilter{Key: "app", Val: "152511"}
    filtermap := &coreinterfaces.FilterMap{}
    _ = filtermap.AddFilter(fieldfilter) // <--- Exception is raised here
}

ランタイムは、以下の理由で、言及された行を受け付けません。

fieldfilter (type *coreinterfaces.FieldFilter) を type として使用することはできません。 fieldint.AddFilter の引数に *coreinterfaces.FilterInterface を指定してください。 *coreinterfaces.FilterInterface はインターフェースへのポインターであり、インターフェースではありません"

しかし、このコードを変更すると

func DoBid() error {
    bs := string(b)
    var ifilterfield coreinterfaces.FilterInterface
    fieldfilter := &coreinterfaces.FieldFilter{Key: "app", Val: "152511"}
    ifilterfield = fieldfilter
    filtermap := &coreinterfaces.FilterMap{}
    _ = filtermap.AddFilter(&ifilterfield)
}

すべて問題なく、アプリケーションをデバッグすると、本当に含まれているようです。

このトピックについては、少し混乱しています。このまったく同じ問題を議論している他のブログ記事やスタックオーバーフローのスレッドを見てみると、(たとえば これは または この なぜなら、fieldfilter と fieldmap は両方ともインターフェースの値ではなく、インターフェースへのポインターとして初期化されるからです。FieldInterfaceを宣言してそのインターフェイスの実装を割り当てないようにするために、ここで実際に何が起こるのかを理解することができません。これを行うためのエレガントな方法があるはずです。

解決方法は?

つまり、あなたはここで2つの概念を混同しているのです。 構造体へのポインタとインタフェースへのポインタは同じではありません。 インターフェースは構造体を直接格納することもできますし または 構造体へのポインタ 後者の場合、やはりインターフェースを直接使うだけです。 ではなく へのポインタです。 例えば

type Fooer interface {
    Dummy()
}

type Foo struct{}

func (f Foo) Dummy() {}

func main() {
    var f1 Foo
    var f2 *Foo = &Foo{}

    DoFoo(f1)
    DoFoo(f2)
}

func DoFoo(f Fooer) {
    fmt.Printf("[%T] %+v\n", f, f)
}

出力します。

[main.Foo] {}
[*main.Foo] &{}

https://play.golang.org/p/I7H_pv5H3Xl

どちらの場合も f 変数に DoFoo は単なるインターフェイスです。 ではなく へのポインタです。 しかし f2 の場合、インターフェース ホールド へのポインタが Foo 構造体を作成します。

インターフェースへのポインターは、ほとんど 決して は有用です。 実際、Goランタイムは数バージョン前に特に変更され、(構造体ポインタの場合のように)インターフェイスポインタを自動的に非参照にしないようになりました。 圧倒的多数の場合、インターフェイスへのポインターは、インターフェイスがどのように機能することになっているのかについての誤解を反映しています。

ただし、インターフェースには制限があります。 インターフェイスに直接構造体を渡すと、その構造体には その型のメソッド(つまり func (f Foo) Dummy() ではなく func (f *Foo) Dummy() ) を使ってインターフェースを満たすことができます。 これは,インターフェイスに元の構造のコピーを保存しているため,ポインタのメソッドは予期せぬ効果をもたらす(つまり,元の構造を変更できない)ためです. したがって、デフォルトの経験則は 構造体へのポインタをインターフェイスに格納する やむを得ない理由がない限り、そうする必要があります。

具体的には、あなたのコードで、AddFilter関数のシグネチャを変更すると。

func (fp *FilterMap) AddFilter(f FilterInterface) uuid.UUID

そして、GetFilterByIDのシグネチャに。

func (fp *FilterMap) GetFilterByID(i uuid.UUID) FilterInterface

あなたのコードは期待通りに動作します。 fieldfilter は、タイプ *FieldFilter を満たすものである。 FilterInterface インタフェース型であるため AddFilter はそれを受け入れる。

Goのメソッド、型、インターフェースがどのように機能し、互いに統合されているかを理解するための良い参考文献をいくつか紹介します。