it-swarm.com.de

"<Typ> ist ein Verweis auf die Schnittstelle, nicht auf die Schnittstelle"

Liebe Entwickler,

Ich habe dieses Problem, das mir etwas komisch vorkommt. Schauen Sie sich dieses Codefragment an:

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
}

Bei einem anderen Paket habe ich den folgenden Code:

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

Die Laufzeit akzeptiert die angegebene Zeile nicht, weil 

"Feldfilter (Typ * coreinterfaces.FieldFilter) kann nicht als Typ verwendet werden * coreinterfaces.FilterInterface im Argument für fieldint.AddFilter: * coreinterfaces.FilterInterface ist Zeiger auf Schnittstelle, nicht Schnittstelle"

Wenn Sie jedoch den Code ändern in:

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)
}

Alles ist in Ordnung und beim Debuggen der Anwendung scheint es wirklich zu gehören 

Ich bin zu diesem Thema etwas verwirrt. Bei der Betrachtung anderer Blogbeiträge und Stack-Überlauf-Threads, die genau dasselbe Problem behandeln (zum Beispiel - This oder This ), sollte der erste Ausschnitt, der diese Ausnahme auslöst, funktionieren, da sowohl Feldfilter als auch Fieldmap funktionieren werden als Zeiger auf Schnittstellen initialisiert und nicht als Wert von Schnittstellen. Ich konnte mich nicht damit beschäftigen, was hier tatsächlich passiert und dass ich ändern muss, um kein FieldInterface zu deklarieren und die Implementierung für dieses Interface zuzuweisen. Dafür muss es einen eleganten Weg geben.

43
0rka

Sie verwirren hier also zwei Konzepte. Ein Zeiger auf eine Struktur und ein Zeiger auf eine Schnittstelle sind nicht identisch. Eine Schnittstelle kann entweder eine Struktur direkt odereinen Zeiger auf eine Struktur speichern. Im letzteren Fall verwenden Sie die Schnittstelle immer noch direkt, nicht einen Zeiger auf die Schnittstelle. Zum Beispiel:

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)
}

Ausgabe: 

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

https://play.golang.org/p/BGV9d1-IRW

In beiden Fällen ist die Variable f in DoFoo nur eine Schnittstelle, nicht ein Zeiger auf eine Schnittstelle. Beim Speichern von f2 hält die Schnittstelle hält einen Zeiger auf eine Foo-Struktur.

Zeiger auf Schnittstellen sind fast niemals nützlich. Tatsächlich wurde die Go-Laufzeitumgebung einige Versionen speziell dahingehend geändert, dass Schnittstellenzeiger nicht mehr automatisch dereferenziert werden (wie dies bei Strukturzeigern der Fall ist), um ihre Verwendung zu verhindern. In der überwiegenden Mehrheit der Fälle spiegelt ein Zeiger auf eine Schnittstelle ein Missverständnis darüber wider, wie Schnittstellen funktionieren sollen.

Es gibt jedoch eine Einschränkung für Schnittstellen. Wenn Sie eine Struktur direkt an eine Schnittstelle übergeben, können nur value - Methoden dieses Typs (dh func (f Foo) Foo(), nicht func (f *Foo) Foo()) zur Ausführung der Schnittstelle verwendet werden. Dies liegt daran, dass Sie eine Kopie der Originalstruktur in der Benutzeroberfläche speichern. Zeigermethoden hätten unerwartete Auswirkungen (dh, die Originalstruktur kann nicht geändert werden). Daher ist die Standard-Faustregel, Zeiger auf Strukturen in Schnittstellen zu speichern, es sei denn, es gibt einen zwingenden Grund, dies nicht zu tun.

Speziell für Ihren Code, wenn Sie die Signatur der AddFilter-Funktion in Folgendes ändern:

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

Und die GetFilterByID-Signatur an:

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

Ihr Code wird wie erwartet funktionieren. fieldfilter ist vom Typ *FieldFilter, wodurch der FilterInterface-Interfacetyp erfüllt wird. AddFilter akzeptiert ihn.

Hier einige gute Referenzen, um zu verstehen, wie Methoden, Typen und Schnittstellen in Go funktionieren und miteinander integriert werden:

69
Kaedys
GetFilterByID(i uuid.UUID) *FilterInterface

Wenn ich diesen Fehler erhalte, liegt dies normalerweise daran, dass ich einen Zeiger auf eine Schnittstelle anstelle einer Schnittstelle angereife (dies ist tatsächlich ein Zeiger auf meine Struktur, die die Schnittstelle erfüllt). 

Es gibt eine gültige Verwendung für * interface {...}, aber häufiger denke ich nur, dass dies ein Zeiger ist, statt dass dies eine Schnittstelle ist, die zufällig ein Zeiger im Code ist, den ich schreibe.

Ich habe es einfach rausgeschmissen, weil die akzeptierte Antwort, obwohl sie detailliert war, mir nicht bei der Fehlersuche half.

0
Dan Farrell