Ten błąd czasu kompilacji pojawia się, gdy próbujesz przypisać lub przekazać (lub przekonwertować) konkretny typ na typ interfejsu; a sam typ nie implementuje interfejsu, a jedynie wskaźnik do typu .
Zobaczmy przykład:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Stringer
Typu interfejs ma tylko jedną metodę: String()
. Każda wartość przechowywana w wartości interfejsu Stringer
musi mieć tę metodę. Stworzyliśmy również MyType
i stworzyliśmy metodę MyType.String()
z odbiornikiem wskaźnika . Oznacza to, że String()
metoda ta jest w zestawie metody w *MyType
rodzaju, ale nie na tym, że od MyType
.
Kiedy próbujemy przypisać wartość MyType
zmiennej typu Stringer
, otrzymujemy następujący błąd:
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)
Ale wszystko jest w porządku, jeśli spróbujemy przypisać wartość typu *MyType
do Stringer
:
s = &m
fmt.Println(s)
I otrzymujemy oczekiwany wynik (wypróbuj go na Go Playground ):
something
Więc wymagania, aby uzyskać ten błąd czasu kompilacji:
- Przypisywana (lub przekazywana lub konwertowana) wartość typu non-point konkretnych typów
- Typ interfejsu przypisywany (przekazywany do lub konwertowany na)
- Konkretny typ ma wymaganą metodę interfejsu, ale z odbiornikiem wskaźnika
Możliwości rozwiązania problemu:
- Należy użyć wskaźnika do wartości, którego zestaw metod będzie obejmował metodę z odbiornikiem wskaźnika
- Lub typ odbiornika musi zostać zmieniony na non-pointer , więc zestaw metod typu non-point-beton będzie również zawierał metodę (a tym samym spełnia interfejs). Może to być, ale nie musi, opłacalne, ponieważ jeśli metoda musi zmodyfikować wartość, odbiornik bez wskaźnika nie jest opcją.
Struktury i osadzanie
Podczas używania struktur i osadzania często nie jest to „ty”, który implementuje interfejs (zapewnia implementację metody), ale typ, który osadzasz w swoim struct
. Jak w tym przykładzie:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
Znowu błąd czasu kompilacji, ponieważ zestaw metod MyType2
nie zawiera String()
metody osadzonej MyType
, tylko zestaw metod *MyType2
, więc następujące działania (wypróbuj na Go Playground ):
var s Stringer
s = &m2
Możemy również sprawić, że będzie działał, jeśli osadzimy *MyType
i użyjemy tylko wskaźnika innego MyType2
(wypróbuj go na Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
Ponadto, niezależnie od tego, co osadzimy (albo, MyType
albo *MyType
), jeśli użyjemy wskaźnika *MyType2
, zawsze będzie działać (wypróbuj go na Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
Odpowiednia sekcja ze specyfikacji (z sekcji Typy konstrukcji ):
Biorąc pod uwagę typ struktury S
i nazwany typ T
, promowane metody są zawarte w zestawie metod struktury w następujący sposób:
- Jeśli
S
zawiera anonimowe pole T
, zestawy metod S
i *S
oba zawierają promowane metody z odbiornikiem T
. Zestaw metod *S
obejmuje również promowane metody z odbiornikiem *T
.
- Jeśli
S
zawiera anonimowe pole *T
, zestawy metod S
i *S
oba zawierają promowane metody z odbiornikiem T
lub *T
.
Innymi słowy: jeśli osadzimy typ nieinterpretacyjny, zestaw metod embedera nieinterpretacyjnego pobiera metody tylko z odbiornikami non-pointer (z typu osadzonego).
Jeśli osadzimy typ wskaźnika, zestaw metod modułu osadzającego niebędącego wskaźnikiem pobiera metody zarówno z odbiornikami wskaźnikowymi, jak i nie wskaźnikowymi (z typu osadzonego).
Jeśli użyjemy wartości wskaźnika do narzędzia osadzającego, niezależnie od tego, czy typ osadzony jest wskaźnikiem, czy nie, zestaw metod wskaźnika do narzędzia osadzającego zawsze pobiera metody zarówno z odbiornikiem wskaźnika, jak i odbiornika niebędącego wskaźnikiem (z typu osadzonego).
Uwaga:
Jest bardzo podobny przypadek, a mianowicie, gdy mają wartość interfejsu, który otacza wartość MyType
i spróbować wpisać assert inną wartość interfejsu z nim Stringer
. W takim przypadku twierdzenie nie zostanie zachowane z powodów opisanych powyżej, ale otrzymamy nieco inny błąd wykonania:
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
Runtime panic (wypróbuj na Go Playground ):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Próbując przekonwertować zamiast assert, otrzymujemy błąd kompilacji, o którym mówimy:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
func (m *MyType)
”, Albo żadnymi . Czy tak jest Czy mogę łączyć różne rodzaje „funkcji składowych”, np.func (m *MyType)
&func (m MyType)
?