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)
?MyType
, jeśli nie możesz zmienićMyType
metody wartości. Próbowałem czegoś takiego,(&i).(*Stringer)
ale to nie działa. Czy to w ogóle możliwe?x := i.(MyType)
A następnie możesz wywoływać metody z odbiornikiem wskaźnika, np.i.String()
Co jest skrótem, dla(&i).String()
którego udaje się, ponieważ zmienne są adresowalne. Ale metoda wskaźnika zmieniająca wartość (wskazaną wartość) nie zostanie odzwierciedlona w wartości opakowanej w wartość interfejsu, dlatego nie ma to większego sensu.*T
nie są zawarte w zestawie metod,S
ponieważS
mogą nie być adresowalne (np. Wartość zwracana przez funkcję lub wynik indeksowania mapy), a także dlatego, że często jest dostępna / odbierana tylko kopia, a jeśli przyjmowanie jej adresu jest dozwolone, metoda ze wskaźnikiem odbiornik może tylko modyfikować kopię (zamieszanie, jakbyś założył, że oryginał został zmodyfikowany). Zobacz tę odpowiedź na przykład: Korzystanie z odbicia SetString .Krótko mówiąc, powiedzmy, że masz ten kod i masz interfejs modułu ładującego oraz moduł WebLoader, który implementuje ten interfejs.
Więc ten kod da ci ten błąd czasu kompilacji
Musisz tylko zmienić
webLoader := WebLoader{}
na następujące:Dlaczego więc to naprawi, ponieważ zdefiniujesz tę funkcję,
func (w *WebLoader) Load
aby zaakceptować odbiornik wskaźnika. Aby uzyskać więcej wyjaśnień, przeczytaj odpowiedzi @icza i @karoraźródło
Innym przypadkiem, gdy widziałem, że coś takiego się dzieje, jest to, że chcę utworzyć interfejs, w którym niektóre metody zmodyfikują wartość wewnętrzną, a inne nie.
Coś, co następnie implementuje ten interfejs, może wyglądać tak:
Tak więc typ implementacyjny prawdopodobnie będzie miał pewne metody, które są odbiornikami wskaźników, a niektóre nie, a ponieważ mam dość różnorodne te różne rzeczy, które są GetterSetters, chciałbym sprawdzić w moich testach, czy wszystkie wykonują oczekiwane.
Gdybym miał zrobić coś takiego:
Wtedy nie dostanę wyżej wspomnianego błędu „X nie implementuje Y (metoda Z ma odbiornik wskaźnika)” (ponieważ jest to błąd czasu kompilacji), ale będę miał zły dzień, ścigając dokładnie, dlaczego mój test się nie udaje. .
Zamiast tego muszę się upewnić, że sprawdzam typ za pomocą wskaźnika, takiego jak:
Lub:
Wtedy wszystko jest zadowolone z testów!
Ale poczekaj! W moim kodzie być może mam metody, które gdzieś akceptują GetterSetter:
Jeśli wywołam te metody z wewnątrz metody innego typu, wygeneruje to błąd:
Każde z poniższych połączeń będzie działać:
źródło