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
Simon Nickerson
źródło
1
Zobacz także stackoverflow.com/q/20874798/260805
Ztyx,
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
Denys Séguret
źródło
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 .

zzzz
źródło
Należy to zaakceptować, a drugie tak naprawdę nie odpowiada na pytanie.
DrKey
0

Oto bardzo prosty sposób przypisania struktury do interfejsu:

package main

type Interface interface{}

type Struct struct{}

func main() {
    ps := new(Struct)
    pi := Interface(ps)

    _, _ = pi, ps
}

https://play.golang.org/p/BRTaTA5AG0S

Miguel Mota
źródło
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

STAL
źródło