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