Warum kann ich einer * Schnittstelle keine * Struktur zuweisen?

142

Ich arbeite gerade an der Go-Tour und bin verwirrt über Zeiger und Schnittstellen. Warum wird dieser Go-Code nicht kompiliert?

package main

type Interface interface {}

type Struct struct {}

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

    _, _ = pi, ps
}

dh wenn Structist ein Interface, warum sollte ein nicht ein *Structsein *Interface?

Die Fehlermeldung, die ich erhalte, lautet:

prog.go:10: cannot use ps (type *Struct) as type *Interface in assignment:
        *Interface is pointer to interface, not interface
Simon Nickerson
quelle
es sieht so aus, als könnten sich die Schnittstellen wie implizite Zeiger verhalten ...
Victor
Darf ich vorschlagen, Ihren Spielplatz mit zu bereichern 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 }und Ihre eigenen Schlussfolgerungen zu ziehen
Victor

Antworten:

183

Wenn eine Struktur eine Schnittstelle implementiert, implementiert ein Zeiger auf diese Struktur automatisch auch diese Schnittstelle. Aus diesem Grund haben Sie nie einen *SomeInterfacePrototyp von Funktionen, da dies nichts hinzufügen würde SomeInterface, und Sie benötigen keinen solchen Typ in der Variablendeklaration (siehe diese verwandte Frage ).

Ein Schnittstellenwert ist nicht der Wert der konkreten Struktur (da er eine variable Größe hat, wäre dies nicht möglich), sondern eine Art Zeiger (genauer gesagt ein Zeiger auf die Struktur und ein Zeiger auf den Typ ). Russ Cox beschreibt es genau hier :

Schnittstellenwerte werden als Zwei-Wort-Paar dargestellt, das einen Zeiger auf Informationen über den in der Schnittstelle gespeicherten Typ und einen Zeiger auf die zugehörigen Daten gibt.

Geben Sie hier die Bildbeschreibung ein

Dies ist der Grund Interfaceund nicht *Interfaceder richtige Typ, um einen Zeiger auf eine implementierte Struktur zu halten Interface.

Sie müssen also einfach verwenden

var pi Interface
Denys Séguret
quelle
8
OK, ich denke das macht Sinn für mich. Ich frage mich nur, warum (in diesem Fall) es nicht einfach ein Fehler bei der Kompilierung ist var pi *Interface.
Simon Nickerson
1
Ich ging näher darauf ein, um es zu erklären. Siehe Bearbeiten. Ich schlage vor, den Artikel von Russ Cox zu lesen, auf den ich verweise.
Denys Séguret
1
Dies hat mir nur geholfen, Zeiger so zu verstehen, wie ich es in C oder C ++ nie konnte ... vielen Dank für diese elegante und einfache Erklärung :-)
mindplay.dk
2
Okay, ich verstehe immer noch nicht, warum *SomeInterfacees nicht einfach ein Kompilierungsfehler ist.
Sazary
2
@charneykaye Du bist hier nicht ganz richtig. Sie haben niemals * SomeInterface, wenn Sie eine Schnittstellenvariable deklarieren oder wenn Sie einen Schnittstellentyp als Teil einer Funktionsdeklaration zurückgeben . Allerdings können Sie innerhalb einer Funktion Parameter * SomeInterface haben .
Arauter
7

Das haben Sie vielleicht gemeint:

package main

type Interface interface{}

type Struct struct{}

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

        _, _ = pi, ps
}

Kompiliert OK. Siehe auch hier .

zzzz
quelle
Dies sollte akzeptiert werden, der andere beantwortet die Frage nicht wirklich.
DrKey
0

Hier ist eine sehr einfache Möglichkeit, einer Schnittstelle eine Struktur zuzuweisen:

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
quelle
0

Ich verwende die folgende Methode, interface{}während ich nur eventsI interface{}als Argumente konsumiere , aber ich bin immer noch in der Lage, Strukturzeiger zu senden, wie Sie unten sehen können.

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)

Jetzt in der storyboard.goDatei Create-Funktion

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
}

Wie Sie oben sehen können, verbraucht das Storyboard.go nur, Events []interface{}aber tatsächlich ist das Senden ein Strukturzeiger und es funktioniert gut.

ein weiteres Beispiel hier

STAHL
quelle