X nie implementuje y (...metoda posiada odbiornik wskaźnika)

Jest już kilka Q & jak na tym " X nie implementuje Y (... metoda ma odbiornik wskaźnika) " rzecz, ale dla mnie, wydaje się, że mówią o różnych rzeczach, a nie stosuje się do mojego konkretnego przypadku.

Więc zamiast stawiać pytanie bardzo konkretnie, robię je szeroko i abstrakcyjnie -- wydaje się, że jest kilka różnych przypadków, które mogą spowodować ten błąd, może ktoś to podsumować?

Czyli jak uniknąć problemu, a jeśli do niego dojdzie, to co czy są możliwości? Thx.

Author: Flimzy, 2016-11-27

3 answers

Ten błąd w czasie kompilacji pojawia się, gdy próbujesz przypisać lub przekazać (lub przekonwertować) konkretny typ do typu interfejsu; a sam typ nie implementuje interfejsu, tylko 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 }

Typ interfejsu Stringer ma tylko jedną metodę: String(). Każda wartość przechowywana w wartości interfejsu Stringer musi posiadać tę metodę. Stworzyliśmy również metodę MyType i stworzyliśmy metodę MyType.String() ze wskaźnikiem odbiornik. Oznacza to, że metoda String() znajduje się w zbiorze metod typu *MyType, ale nie w zbiorze metod MyType.

Gdy spróbujemy przypisać wartość MyType do zmiennej typu Stringer, otrzymujemy błąd o którym mowa:

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 ok jeśli spróbujemy przypisać wartość typu *MyType do Stringer:

s = &m
fmt.Println(s)

I otrzymujemy oczekiwany efekt (spróbuj na Go Playground):

something

Więc wymagania, aby uzyskać ten czas kompilacji błąd:

  • wartość Nie-wskaźnik konkretny typ jest przypisany (lub przekazany lub przekształcony)
  • W tym samym czasie, w zależności od tego, co dzieje się w danym interfejsie, następuje jego konwersja.]} [[77]}Typ betonu 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 zawierał metodę ze wskaźnikiem odbiornik
  • lub typ odbiornika musi zostać zmieniony na non-pointer , więc zestaw metod typu non-pointer concrete będzie również zawierał metodę(i tym samym spełniał interfejs). Może to być, ale nie może być wykonalne, tak jakby metoda musiała zmodyfikować wartość, odbiornik nie-wskaźnikowy nie jest opcją.

Struktury i osadzanie

Podczas używania struktur i osadzania , często to nie "ty" implementujesz interfejs (podaj metodę implementacja), ale Typ osadzony w 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

Ponownie błąd w czasie kompilacji, ponieważ zestaw metod MyType2 nie zawiera metody String() wbudowanej MyType, tylko zestaw metod *MyType2, więc następujące działania (spróbuj na Go Playground):

var s Stringer
s = &m2

Możemy również sprawić, że zadziała, jeśli umieścimy *MyType i użyjemy tylko non-pointera MyType2 (spróbuj na Go Playground):

type MyType2 struct {
    *MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: &m}

var s Stringer
s = m2

Także, cokolwiek umieścimy (MyType lub *MyType), jeśli użyjemy wskaźnika *MyType2, zawsze będzie działać (spróbuj na Go Playground):

type MyType2 struct {
    *MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: &m}

var s Stringer
s = &m2

Odpowiednia sekcja z spec (z sekcji typy struktur):

Biorąc pod uwagę typ struktury S i typ o nazwie T, promowane metody są zawarte w zbiorze metod struktury w następujący sposób:

  • jeśli S zawiera anonimowe pole T, zbiory metod S i *S zawierają promowane metody z odbiornikiem T. Zbiór metod *S zawiera również promowane metody z odbiornikiem *T.
  • jeśli S zawiera anonimowe pole *T, zbiory metod S i *S zawierają promowane metody z odbiornikiem T lub *T.

Innymi słowy: jeśli umieścimy Typ non-pointer, zestaw metod osadzania non-pointer pobiera tylko metody z odbiornikami non - pointer (z typu embedded).

Jeśli embed a pointer type, the method set of the non-pointer embedder gets methods with both pointer and non-pointer receivers (from the embedded type).

Jeśli użyjemy wartości wskaźnika do embeddera, niezależnie od tego, czy typ osadzony jest wskaźnikiem, czy nie, zestaw metod wskaźnika do embeddera zawsze pobiera metody zarówno z odbiornikami wskaźnika, jak i nie-wskaźnikowymi (od typu osadzonego).

Uwaga:

Jest bardzo podobny przypadek, mianowicie gdy masz wartość interfejsu, która otacza wartość MyType i próbujesz wpisać assert inną wartość interfejsu z niego, Stringer. W tym przypadku twierdzenie nie utrzyma się z powodów opisanych powyżej, ale otrzymamy nieco inny runtime-error:

m := MyType{value: "something"}

var i interface{} = m
fmt.Println(i.(Stringer))

Runtime panic (try it on the Go Playground):

panic: interface conversion: main.MyType is not main.Stringer:
    missing method String

Próbując przekonwertować zamiast type assert, otrzymujemy błąd w czasie kompilacji, o którym mówimy:

m := MyType{value: "something"}

fmt.Println(Stringer(m))
 156
Author: icza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-30 20:38:13

Krótko mówiąc, powiedzmy, że masz ten kod i masz interfejs loadera i WebLoader, który implementuje ten interfejs.

package main

import "fmt"

// Loader defines a content loader
type Loader interface {
    Load(src string) string
}

// WebLoader is a web content loader
type WebLoader struct{}

// Load loads the content of a page
func (w *WebLoader) Load(src string) string {
    return fmt.Sprintf("I loaded this page %s", src)
}

func main() {
    webLoader := WebLoader{}
    loadContent(webLoader)
}

func loadContent(loader Loader) {
    loader.Load("google.com")
}

Więc ten kod da ci ten błąd czasu kompilacji

./ mainidź:20: 13: nie można używać webloadera (typu WebLoader) jako typu Loader w argumencie do loadContent: WebLoader nie implementuje loadera (metoda Load posiada odbiornik wskaźnika)

Więc to, co musisz zrobić, to zmienić webLoader := WebLoader{} na następujące:

webLoader := &WebLoader{} 

Więc dlaczego to naprawi, ponieważ zdefiniujesz tę funkcję func (w *WebLoader) Load, aby akceptowała odbiornik wskaźnika. Aby uzyskać więcej wyjaśnień, przeczytaj odpowiedzi @icza i @karora

 7
Author: Saman Shafigh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-21 07:51:47

Inny przypadek, kiedy widziałem tego rodzaju rzeczy dzieje się, jeśli chcę stworzyć interfejs, gdzie niektóre metody będą modyfikować wewnętrzną wartość, a inne nie.

type GetterSetter interface {
   GetVal() int
   SetVal(x int) int
}

Coś, co następnie implementuje ten interfejs, może wyglądać następująco:

type MyTypeA struct {
   a int
}

func (m MyTypeA) GetVal() int {
   return a
}

func (m *MyTypeA) SetVal(newVal int) int {
    int oldVal = m.a
    m.a = newVal
    return oldVal
}

Więc Typ implementacji prawdopodobnie będzie miał kilka metod, które są odbiornikami wskaźnikowymi, a niektóre nie, a ponieważ mam dość wiele tych różnych rzeczy, które są GetterSetters chciałbym sprawdzić w moich testach, że są one wszystko działa zgodnie z oczekiwaniami.

Gdybym miał zrobić coś takiego:

myTypeInstance := MyType{ 7 }
... maybe some code doing other stuff ...
var f interface{} = myTypeInstance
_, ok := f.(GetterSetter)
if !ok {
    t.Fail()
}

Wtedy nie dostanę wyżej wspomnianego błędu " X nie implementuje Y (Z metoda ma odbiornik wskaźnika)" (ponieważ jest to błąd w czasie kompilacji), ale będzie mieć zły dzień goniąc dokładnie dlaczego mój test zawodzi...

Zamiast tego muszę się upewnić, że sprawdzam Typ za pomocą wskaźnika, na przykład:

var f interface{} = new(&MyTypeA)
 ...

Lub:

myTypeInstance := MyType{ 7 }
var f interface{} = &myTypeInstance
...

Więc wszyscy są zadowoleni z testów!

Ale czekaj! W moim kod, być może mam metody, które akceptują gdzieś GetterSetter:

func SomeStuff(g GetterSetter, x int) int {
    if x > 10 {
        return g.GetVal() + 1
    }
    return g.GetVal()
}

Jeśli wywołam te metody z innej metody typu, wygeneruje to błąd:

func (m MyTypeA) OtherThing(x int) {
    SomeStuff(m, x)
}

Jedno z następujących wywołań będzie działać:

func (m *MyTypeA) OtherThing(x int) {
    SomeStuff(m, x)
}

func (m MyTypeA) OtherThing(x int) {
    SomeStuff(&m, x)
}
 3
Author: karora,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-13 19:03:03