Dlaczego nie mogę przypisać * Struct do * Interface?


142

Właśnie pracuję nad trasą Go i jestem zdezorientowany co do wskaźników i interfejsów. Dlaczego ten kod Go nie kompiluje się?

package main

type Interface interface {}

type Struct struct {}

func main() {
    var ps *Struct
    var pi *Interface
    pi = ps

    _, _ = pi, ps
}

tj. jeśli Structjest Interface, to dlaczego nie *Structbyłoby a *Interface?

Otrzymuję komunikat o błędzie:

prog.go:10: cannot use ps (type *Struct) as type *Interface in assignment:
        *Interface is pointer to interface, not interface


wygląda na to, że interfejsy mogą zachowywać się jak ukryte wskaźniki ...
Victor,

czy mogę zasugerować wzbogacenie placu zabaw func main() { var ps *Struct = new(Struct) var pi *Interface var i Interface i = ps pi = &i fmt.Printf("%v, %v, %v\n", *ps, pi, &i) i = *ps fmt.Printf("%v, %v, %v\n", *ps, pi, i) _, _, _ = i, pi, ps }i tworzenie własnych konkluzji
Victor,

Odpowiedzi:


183

Kiedy masz strukturę implementującą interfejs, wskaźnik do tej struktury automatycznie implementuje również ten interfejs. Dlatego nigdy nie masz *SomeInterfacew prototypie funkcji, ponieważ to by niczego nie dodało SomeInterfacei nie potrzebujesz takiego typu w deklaracji zmiennej (zobacz to powiązane pytanie ).

Wartość interfejsu nie jest wartością konkretnej struktury (ponieważ ma zmienny rozmiar, nie byłoby to możliwe), ale jest rodzajem wskaźnika (dokładniej jest wskaźnikiem do struktury i wskaźnikiem do typu ). Russ Cox opisuje to dokładnie tutaj :

Wartości interfejsu są reprezentowane jako para dwóch słów, dająca wskaźnik do informacji o typie przechowywanym w interfejsie i wskaźnik do powiązanych danych.

wprowadź opis obrazu tutaj

Dlatego właśnie Interface, a nie, *Interfacejest prawidłowym typem do przechowywania wskaźnika do implementacji struktury Interface.

Musisz więc po prostu użyć

var pi Interface

8
OK, myślę, że to ma dla mnie sens. Zastanawiam się tylko, dlaczego (w tym przypadku) nie jest to po prostu błąd czasu kompilacji var pi *Interface.
Simon Nickerson

1
Podałem więcej szczegółów, aby to wyjaśnić. Zobacz edycję. Proponuję przeczytać artykuł Russa Coxa, do którego odsyłam.
Denys Séguret

1
To właśnie pomogło mi zrozumieć wskazówki w sposób, którego nigdy nie byłem w stanie zrobić w C lub C ++ ... bardzo dziękuję za to eleganckie i proste wyjaśnienie :-)
mindplay.dk

2
W porządku, nadal nie rozumiem, dlaczego *SomeInterfacenie jest to po prostu błąd kompilacji?
sazary

2
@charneykaye Nie masz tu do końca racji. Nigdy nie ma * SomeInterface podczas deklarowania zmiennej interfejsu lub zwracania typu interfejsu jako części deklaracji funkcji . Jednak możesz mieć * SomeInterface w parametrach funkcji .
arauter

7

Być może to miałeś na myśli:

package main

type Interface interface{}

type Struct struct{}

func main() {
        var ps *Struct
        var pi *Interface
        pi = new(Interface)
        *pi = ps

        _, _ = pi, ps
}

Kompiluje się OK. Zobacz także tutaj .


Należy to zaakceptować, a drugie tak naprawdę nie odpowiada na pytanie.
DrKey


0

Używam następującego sposobu, interface{}podczas gdy po prostu konsumuję eventsI interface{}jako argumenty, nadal jestem w stanie wysłać wskaźniki Struct, jak widać poniżej.

func Wait(seconds float64) *WaitEvent {
    return WaitEventCreate(seconds)
}

main.go

var introScene = []interface{}{
        storyboard.Wait(5),
        storyboard.Wait(2),
    }

    var storyboardI = storyboard.Create(stack, introScene)
    stack.Push(&storyboardI)

Teraz wewnątrz storyboard.gopliku Create function

type Storyboard struct {
    Stack  *gui.StateStack
    Events []interface{} //always keep as last args
}

func Create(stack *gui.StateStack, eventsI interface{}) Storyboard {
    sb := Storyboard{
        Stack: stack,
    }

    if eventsI != nil {
        events := reflect.ValueOf(eventsI)
        if events.Len() > 0 {
            sb.Events = make([]interface{}, events.Len())
            for i := 0; i < events.Len(); i++ {
                sb.Events[i] = events.Index(i).Interface()
            }
        }
    }

    return sb
}

Jak widać powyżej, Storyboard.go zużywa tylko, Events []interface{}ale w rzeczywistości wysyłam wskaźnik Struct i działa dobrze.

kolejny przykład tutaj

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.