"jest wskaźnikiem do interfejsu, a nie interfejsem"

Mam taki problem, który wydaje mi się trochę dziwny. Spójrz na ten fragment kodu:

package coreinterfaces

type FilterInterface interface {
    Filter(s *string) bool
}

type FieldFilter struct {
    Key string
    Val string
}

func (ff *FieldFilter) Filter(s *string) bool {
    // Some code
}

type FilterMapInterface interface {
    AddFilter(f *FilterInterface) uuid.UUID     
    RemoveFilter(i uuid.UUID)                   
    GetFilterByID(i uuid.UUID) *FilterInterface
}

type FilterMap struct {
    mutex   sync.Mutex
    Filters map[uuid.UUID]FilterInterface
}

func (fp *FilterMap) AddFilter(f *FilterInterface) uuid.UUID {
    // Some code
}

func (fp *FilterMap) RemoveFilter(i uuid.UUID) {
    // Some code
}

func (fp *FilterMap) GetFilterByID(i uuid.UUID) *FilterInterface {
    // Some code
}

Na jakimś innym pakiecie mam następujący kod:

func DoFilter() {
    fieldfilter := &coreinterfaces.FieldFilter{Key: "app", Val: "152511"}
    filtermap := &coreinterfaces.FilterMap{}
    _ = filtermap.AddFilter(fieldfilter) // <--- Exception is raised here
}

Run-time nie zaakceptuje wspomnianej linii, ponieważ

" nie można użyć fieldfilter (Typ * coreinterfaces.FieldFilter) jako typ * coreinterfaces.FilterInterface w argumencie do fieldint.AddFilter: * coreinterfaces.FilterInterface jest wskaźnikiem do interfejsu, a nie interfejsu"

Jednakże, przy zmianie kodu na:

func DoBid() error {
    bs := string(b)
    var ifilterfield coreinterfaces.FilterInterface
    fieldfilter := &coreinterfaces.FieldFilter{Key: "app", Val: "152511"}
    ifilterfield = fieldfilter
    filtermap := &coreinterfaces.FilterMap{}
    _ = filtermap.AddFilter(&ifilterfield)
}

Wszystko jest w porządku i podczas debugowania aplikacji naprawdę wydaje się zawierać

Jestem trochę zdezorientowany w tym temacie. Patrząc na inne posty na blogu i przepełnienie stosu wątków omawiających dokładnie ten sam problem (na przykład - This , lub This ) pierwszy fragment wywołujący ten wyjątek powinien działać, ponieważ zarówno fieldfilter, jak i fieldmap są inicjowane jako wskaźniki do interfejsów, a nie wartości interfejsów. I nie byłem w stanie zawinąć głowy wokół tego, co faktycznie dzieje się tutaj, że muszę zmienić, aby nie zadeklarować FieldInterface i przypisać implementację dla tego interfejsu. Musi być na to elegancki sposób.
Author: Flimzy, 2017-06-05

2 answers

Więc mylisz dwa pojęcia. Wskaźnik do struktury i wskaźnik do interfejsu nie są takie same. Interfejs może przechowywać strukturę bezpośrednio lub jako wskaźnik do struktury. W tym drugim przypadku nadal używasz interfejsu bezpośrednio, , a nie wskaźnika do interfejsu. Na przykład:

type Fooer interface {
    Dummy()
}

type Foo struct{}

func (f Foo) Dummy() {}

func main() {
    var f1 Foo
    var f2 *Foo = &Foo{}

    DoFoo(f1)
    DoFoo(f2)
}

func DoFoo(f Fooer) {
    fmt.Printf("[%T] %+v\n", f, f)
}

Wyjście:

[main.Foo] {}
[*main.Foo] &{}

Https://play.golang.org/p/I7H_pv5H3Xl

W obu przypadkach zmienna f w DoFoo jest tylko interfejs, Nie wskaźnik do interfejsu. Jednak podczas przechowywania f2, interfejs przechowuje wskaźnik do struktury Foo.

Wskaźniki do interfejsów są prawie nigdy użyteczne. W rzeczywistości, runtime Go został specjalnie zmieniony kilka wersji z powrotem do nie automatycznie dereferencji wskaźników interfejsu (jak to ma miejsce w przypadku wskaźników struktury), aby zniechęcić ich użycie. W przeważającej większości przypadków wskaźnik do interfejsu odzwierciedla niezrozumienie sposobu działania interfejsów.

Istnieje jednak ograniczenie dotyczące interfejsów. Jeśli przekazujesz strukturę bezpośrednio do interfejsu, tylko wartość metody tego typu (tj. func (f Foo) Dummy(), Nie func (f *Foo) Dummy()) mogą być użyte do wypełnienia interfejsu. Dzieje się tak dlatego, że przechowujesz kopię oryginalnej struktury w interfejsie, więc metody wskaźników będą miały nieoczekiwane efekty (np. nie można zmienić oryginalnej struktury). Tak więc domyślną zasadą jest przechowuj wskaźniki do struktur w interfejsach , chyba że istnieje przekonujący powód, aby tego nie robić.

Specjalnie z Twoim kodem, jeśli zmienisz podpis funkcji AddFilter na:

func (fp *FilterMap) AddFilter(f FilterInterface) uuid.UUID

I podpis GetFilterByID do:

func (fp *FilterMap) GetFilterByID(i uuid.UUID) FilterInterface

Twój kod będzie działał zgodnie z oczekiwaniami. fieldfilter jest typu *FieldFilter, który wypełnia Typ interfejsu FilterInterface, a zatem {[13] } będzie go akceptował.

Oto kilka dobrych odniesień do zrozumienia, jak metody, typy i interfejsy działają i integrują się ze sobą w Go:

 176
Author: Kaedys,
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
2019-10-15 07:29:23
GetFilterByID(i uuid.UUID) *FilterInterface

Kiedy pojawia się ten błąd, zwykle jest to spowodowane tym, że określam wskaźnik do interfejsu zamiast interfejsu ( będzie to wskaźnik do mojej struktury, która spełnia interfejs ).

Istnieje poprawne użycie interfejsu *{...} ale częściej po prostu myślę "to jest wskaźnik "zamiast" to jest interfejs, który tak się składa, że jest wskaźnikiem w kodzie, który piszę "

Po prostu wyrzuciłem to tam, ponieważ przyjęta odpowiedź, choć szczegółowa, nie pomogła mi rozwiązywanie problemów.

 6
Author: Daniel Farrell,
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
2019-01-28 03:44:54