web-dev-qa-db-ja.com

「<type>はインターフェイスではなくインターフェイスへのポインタです」の混乱

親愛なる仲間の開発者、

私には少し奇妙に思えるこの問題があります。次のコードスニペットをご覧ください。

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
}

ランタイムは上記の行を受け入れません。

「fieldint.AddFilterへの引数でtype * coreinterfaces.FilterInterfaceとしてfieldfilter(type * coreinterfaces.FieldFilter)を使用できません:* 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)
}

すべてが申し分なく、アプリケーションをデバッグするときに実際に含まれているようです

私はこのトピックについて少し混乱しています。このまったく同じ問題を議論している他のブログ投稿とスタックオーバーフロースレッドを見ると(たとえば、- This 、または This )、この例外を発生させる最初のスニペットは動作するはずです。 fieldfilterとfieldmapは、インターフェイスの値ではなく、インターフェイスへのポインタとして初期化されます。 FieldInterfaceを宣言せず、そのインターフェイスに実装を割り当てるために変更する必要がある実際にここで何が起こるかについて頭を包むことができませんでした。これを行うにはエレガントな方法が必要です。

51
0rka

したがって、ここでは2つの概念を混同しています。構造体へのポインタとインターフェイスへのポインタは同じではありません。インターフェイスは、構造体を直接保存することができますまたは構造体へのポインタ。後者の場合、インターフェイスを直接使用するだけで、 not インターフェイスへのポインタです。例えば:

type Fooer interface {
    Foo()
}

type Foo struct{}

func (f Foo) Foo() {}

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/BGV9d1-IRW

どちらの場合も、fDoFoo変数は単なるインターフェイスであり、 not インターフェイスへのポインタです。ただし、f2を格納する場合、インターフェイス holds Foo構造体へのポインターを保持します。

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

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

特にコードで、AddFilter関数のシグネチャを次のように変更した場合:

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

また、GetFilterByID署名は次のことを行います

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

コードは期待どおりに機能します。 fieldfilterの型は*FieldFilterであり、これはFilterInterfaceインターフェース型を完全に埋めるので、AddFilterはそれを受け入れます。

Goでメソッド、タイプ、インターフェースがどのように機能し、相互に統合するかを理解するための優れたリファレンスを次に示します。

81
Kaedys
GetFilterByID(i uuid.UUID) *FilterInterface

このエラーが表示されるのは、通常、インターフェイスではなくインターフェイスへのポインタを指定しているためです(実際には、インターフェイスを満たす構造体へのポインタになります)。

* interface {...}の有効な使用方法はありますが、より一般的には、「これは、私が書いているコード内のポインタであるインターフェイスです」ではなく、「これはポインタです」と考えています。

受け入れられた答えは、詳細ではありますが、トラブルシューティングに役立たなかったので、ただそこに放り出しました。

1
Daniel Farrell