Pourquoi ne puis-je pas affecter une * Struct à une * Interface?

142

Je travaille juste sur la tournée Go , et je suis confus au sujet des pointeurs et des interfaces. Pourquoi ce code Go ne se compile-t-il pas?

package main

type Interface interface {}

type Struct struct {}

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

    _, _ = pi, ps
}

c'est-à-dire si Structest un Interface, pourquoi ne serait-il pas *Structun *Interface?

Le message d'erreur que je reçois est:

prog.go:10: cannot use ps (type *Struct) as type *Interface in assignment:
        *Interface is pointer to interface, not interface
Simon Nickerson
la source
il semble que les interfaces pourraient se comporter comme des pointeurs implicites ...
Victor
puis-je suggérer d'enrichir votre terrain de jeu 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 }et de faire vos propres conclusions
Victor

Réponses:

183

Lorsque vous avez une structure implémentant une interface, un pointeur vers cette structure implémente également automatiquement cette interface. C'est pourquoi vous n'avez jamais *SomeInterfacedans le prototype de fonctions, car cela n'ajouterait rien SomeInterfaceet vous n'avez pas besoin d'un tel type dans la déclaration de variable (voir cette question connexe ).

Une valeur d'interface n'est pas la valeur de la structure concrète (car elle a une taille variable, ce ne serait pas possible), mais c'est une sorte de pointeur (pour être plus précis un pointeur vers la structure et un pointeur vers le type ). Russ Cox le décrit exactement ici :

Les valeurs d'interface sont représentées sous la forme d'une paire de deux mots donnant un pointeur vers des informations sur le type stocké dans l'interface et un pointeur vers les données associées.

entrez la description de l'image ici

C'est pourquoi Interface, et non, *Interfacele type approprié pour contenir un pointeur vers une struct implémentation Interface.

Vous devez donc simplement utiliser

var pi Interface
Denys Séguret
la source
8
OK, je pense que cela a du sens pour moi. Je me demande simplement pourquoi (dans ce cas), ce n'est pas simplement une erreur de compilation à dire var pi *Interface.
Simon Nickerson
1
Je suis entré dans plus de détails pour l'expliquer. Voir modifier. Je suggère la lecture de l'article de Russ Cox auquel je renvoie.
Denys Séguret
1
Cela m'a juste aidé à donner un sens aux pointeurs d'une manière que je n'ai jamais pu faire en C ou C ++ ... merci beaucoup pour cette explication élégante et simple :-)
mindplay.dk
2
D'accord, je ne comprends toujours pas pourquoi ce *SomeInterfacen'est pas simplement une erreur de compilation?
sazary
2
@charneykaye Vous n'avez pas tout à fait raison ici. Vous n'avez jamais * SomeInterface lors de la déclaration d'une variable d'interface ou lors du retour d'un type d'interface dans le cadre d'une déclaration de fonction . Cependant, vous pouvez avoir * SomeInterface dans les paramètres d'une fonction .
arauter
7

C'est peut-être ce que vous vouliez dire:

package main

type Interface interface{}

type Struct struct{}

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

        _, _ = pi, ps
}

Compile OK. Voir aussi ici .

zzzz
la source
Cela doit être accepté, l'autre ne répond pas vraiment à la question.
DrKey
0

Voici un moyen très simple d'attribuer une structure à une interface:

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
la source
0

J'utilise la manière suivante de interface{}tout en consommant juste eventsI interface{}comme arguments, je suis toujours capable d'envoyer des pointeurs de structure comme vous pouvez le voir ci-dessous.

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)

Maintenant dans storyboard.gola fonction de création de fichier

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
}

Comme vous pouvez le voir ci-dessus, le Storyboard.go consomme juste, Events []interface{}mais en fait, l'envoi d'Im est un pointeur Struct et cela fonctionne bien.

un autre exemple ici

ACIER
la source