Dieser Fehler bei der Kompilierung tritt auf, wenn Sie versuchen, einen konkreten Typ einem Schnittstellentyp zuzuweisen oder zu übergeben (oder zu konvertieren) . und der Typ selbst implementiert nicht die Schnittstelle, sondern nur einen Zeiger auf den Typ .
Sehen wir uns ein Beispiel an:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Der Stringer
Schnittstellentyp hat nur eine Methode : String()
. Jeder Wert, der in einem Schnittstellenwert gespeichert ist, Stringer
muss über diese Methode verfügen. Wir haben auch eine MyType
und eine Methode MyType.String()
mit Zeigerempfänger erstellt . Dies bedeutet, dass sich die String()
Methode im Methodensatz des *MyType
Typs befindet, nicht jedoch im MyType
.
Wenn wir versuchen, MyType
einer Variablen vom Typ einen Wert zuzuweisen Stringer
, erhalten wir den fraglichen Fehler:
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)
Aber alles ist in Ordnung , wenn wir versuchen , einen Wert vom Typ zuweisen *MyType
zu Stringer
:
s = &m
fmt.Println(s)
Und wir bekommen das erwartete Ergebnis (probieren Sie es auf dem Go Playground aus ):
something
Die Voraussetzungen, um diesen Fehler bei der Kompilierung zu erhalten:
- Ein Wert des Nicht-Zeiger -Betontyps wird zugewiesen (oder übergeben oder konvertiert).
- Ein Schnittstellentyp, der zugewiesen (oder übergeben oder konvertiert) wird.
- Der konkrete Typ hat die erforderliche Methode der Schnittstelle, jedoch mit einem Zeigerempfänger
Möglichkeiten zur Behebung des Problems:
- Es muss ein Zeiger auf den Wert verwendet werden, dessen Methodensatz die Methode mit dem Zeigerempfänger enthält
- Oder der Empfängertyp muss in Nicht-Zeiger geändert werden , sodass der Methodensatz des Nicht-Zeiger-Betontyps auch die Methode enthält (und somit die Schnittstelle erfüllt). Dies kann sinnvoll sein oder auch nicht, als ob die Methode den Wert ändern muss, ein Nicht-Zeiger-Empfänger ist keine Option.
Strukturen und Einbettung
Bei der Verwendung von Strukturen und beim Einbetten implementieren häufig nicht "Sie" eine Schnittstelle (stellen eine Methodenimplementierung bereit), sondern ein Typ, den Sie in Ihre einbetten struct
. Wie in diesem Beispiel:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
Wiederum ein Fehler beim Kompilieren, da der Methodensatz von MyType2
nicht die String()
Methode des eingebetteten MyType
, sondern nur den Methodensatz von enthält *MyType2
, sodass Folgendes funktioniert (versuchen Sie es auf dem Go Playground ):
var s Stringer
s = &m2
Wir können es auch zum Laufen bringen, wenn wir *MyType
nur einen Nicht-Zeiger einbetten und verwenden MyType2
(versuchen Sie es auf dem Go-Spielplatz ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
Was auch immer wir einbetten (entweder MyType
oder *MyType
), wenn wir einen Zeiger verwenden *MyType2
, wird es immer funktionieren (versuchen Sie es auf dem Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
Relevanter Abschnitt aus der Spezifikation (aus Abschnitt Strukturtypen ):
Bei einem gegebenen Strukturtyp S
und einem benannten Typ T
sind heraufgestufte Methoden wie folgt im Methodensatz der Struktur enthalten:
- Wenn es
S
ein anonymes Feld enthält T
, enthalten die Methodensätze von S
und *S
beide heraufgestufte Methoden mit Empfänger T
. Der Methodensatz von *S
enthält auch gesponserte Methoden mit Empfänger *T
.
- Wenn es
S
ein anonymes Feld enthält *T
, enthalten die Methodensätze von S
und *S
beide heraufgestufte Methoden mit Empfänger T
oder *T
.
Mit anderen Worten: Wenn wir einen Nicht-Zeiger-Typ einbetten, erhält der Methodensatz des Nicht-Zeiger-Einbetters nur die Methoden mit Nicht-Zeiger-Empfängern (vom eingebetteten Typ).
Wenn wir einen Zeigertyp einbetten, erhält der Methodensatz des Nicht-Zeiger-Einbetters Methoden mit Zeiger- und Nicht-Zeiger-Empfängern (vom eingebetteten Typ).
Wenn wir einen Zeigerwert für den Einbettungswert verwenden, unabhängig davon, ob der eingebettete Typ ein Zeiger ist oder nicht, erhält der Methodensatz des Zeigers auf den Einbettungsmodus immer Methoden sowohl mit dem Zeiger- als auch mit dem Nichtzeigerempfänger (vom eingebetteten Typ).
Hinweis:
Es gibt einen sehr ähnlichen Fall, nämlich dann , wenn Sie eine Schnittstelle Wert haben, der einen Wert von Wraps MyType
, und Sie versuchen, assert zu geben eine andere Schnittstelle Wert von ihm, Stringer
. In diesem Fall gilt die Behauptung aus den oben beschriebenen Gründen nicht, aber wir erhalten einen etwas anderen Laufzeitfehler:
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
Laufzeit-Panik (probieren Sie es auf dem Go-Spielplatz ):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Beim Versuch, anstelle des Typs assert zu konvertieren, wird der Fehler angezeigt, von dem wir sprechen:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
func (m *MyType)
", oder keine . Ist es so? Kann ich verschiedene Arten von "Elementfunktionen" mischen, z. B.func (m *MyType)
&func (m MyType)
?MyType
, wenn Sie dieMyType
Verwendung von Wertmethoden nicht ändern können? Ich habe so etwas versucht,(&i).(*Stringer)
aber es funktioniert nicht. Ist es überhaupt möglich?x := i.(MyType)
können Sie dann Methoden mit Zeigerempfänger aufrufen, z. B.i.String()
eine Abkürzung,(&i).String()
die erfolgreich ist, weil Variablen adressierbar sind. Die Zeigermethode, mit der der Wert (der spitze Wert) geändert wird, spiegelt sich jedoch nicht in dem Wert wider, der in den Schnittstellenwert eingeschlossen ist. Deshalb ist dies wenig sinnvoll.*T
sind nicht im Methodensatz von enthalten,S
weil sieS
möglicherweise nicht adressierbar sind (z. B. Funktionsrückgabewert oder Ergebnis der Kartenindizierung), und auch, weil häufig nur eine Kopie vorhanden / empfangen ist und die Verwendung der Adresse zulässig ist mit Zeigerempfänger konnte nur die Kopie geändert werden (Verwirrung, da Sie annehmen würden, dass das Original geändert wurde). In dieser Antwort finden Sie ein Beispiel: Verwenden von Reflection SetString .Nehmen wir an, Sie haben diesen Code und eine Loader-Schnittstelle und einen WebLoader, der diese Schnittstelle implementiert.
Dieser Code gibt Ihnen also diesen Fehler bei der Kompilierung
Sie müssen also nur
webLoader := WebLoader{}
Folgendes ändern :Warum wird das Problem behoben, weil Sie diese Funktion so definieren
func (w *WebLoader) Load
, dass sie einen Zeigerempfänger akzeptiert? Für weitere Erklärungen lesen Sie bitte die Antworten von @icza und @karoraquelle
Ein anderer Fall, in dem ich so etwas gesehen habe, ist, wenn ich eine Schnittstelle erstellen möchte, in der einige Methoden einen internen Wert ändern und andere nicht.
Etwas, das diese Schnittstelle dann implementiert, könnte sein:
Der Implementierungstyp wird wahrscheinlich einige Methoden haben, die Zeigerempfänger sind, und einige, die es nicht sind, und da ich eine ganze Reihe dieser verschiedenen Dinge habe, die GetterSetter sind, möchte ich in meinen Tests überprüfen, ob sie alle die erwarteten Ergebnisse erzielen.
Wenn ich so etwas machen würde:
Dann erhalte ich nicht den oben genannten Fehler "X implementiert Y nicht (Z-Methode hat Zeigerempfänger)" (da es sich um einen Fehler zur Kompilierungszeit handelt), aber ich werde einen schlechten Tag haben, um genau zu verfolgen, warum mein Test fehlschlägt. .
Stattdessen muss ich sicherstellen, dass ich die Typprüfung mit einem Zeiger durchführe, wie z.
Oder:
Dann ist alles mit den Tests zufrieden!
Aber warte! In meinem Code habe ich vielleicht Methoden, die irgendwo einen GetterSetter akzeptieren:
Wenn ich diese Methoden aus einer anderen Typmethode heraus aufrufe, wird der Fehler generiert:
Jeder der folgenden Aufrufe funktioniert:
quelle