Méthode de Golang avec récepteur de pointeur

j'ai ce code d'exemple

package main

import (
    "fmt"
)

type IFace interface {
    SetSomeField(newValue string)
    GetSomeField() string
}

type Implementation struct {
    someField string
}

func (i Implementation) GetSomeField() string {
    return i.someField
}

func (i Implementation) SetSomeField(newValue string) {
    i.someField = newValue
}

func Create() IFace {
    obj := Implementation{someField: "Hello"}
    return obj // <= Offending line
}

func main() {
    a := Create()
    a.SetSomeField("World")
    fmt.Println(a.GetSomeField())
}

SetSomeField ne fonctionne pas comme prévu car son récepteur n'est pas de type pointeur.

Si je change la méthode à un pointeur sur un récepteur, ce que je m'attends à travailler, il ressemble à ceci:

func (i *Implementation) SetSomeField(newValue string) { ...

compiler ceci conduit à l'erreur suivante:

prog.go:26: cannot use obj (type Implementation) as type IFace in return argument:
Implementation does not implement IFace (GetSomeField method has pointer receiver)

Comment puis-je avoir le struct implémenter l'interface et la méthode SetSomeField changer la valeur de l'instance réelle sans la création d'une copie?

Voici Un snippet hackable: https://play.golang.org/p/ghW0mk0IuU

j'ai déjà vu cette question dans go (golang), Comment Pouvez-vous lancer un pointeur d'interface dans un pointeur de struct?, mais je ne vois pas comment il est lié à cet exemple.

18
go
demandé sur Community 2015-11-26 13:23:02

2 réponses

votre pointeur vers la structure devrait implémenter l'Interface. De cette façon, vous pouvez modifier les champs.

Regardez comment j'ai modifié ton code pour qu'il fonctionne comme prévu:

package main

import (
    "fmt"
)

type IFace interface {
    SetSomeField(newValue string)
    GetSomeField() string
}

type Implementation struct {
    someField string
}    

func (i *Implementation) GetSomeField() string {
    return i.someField
}

func (i *Implementation) SetSomeField(newValue string) {
    i.someField = newValue
}

func Create() *Implementation {
    return &Implementation{someField: "Hello"}
}

func main() {
    var a IFace
    a = Create()
    a.SetSomeField("World")
    fmt.Println(a.GetSomeField())
}
25
répondu nessuno 2015-11-26 11:16:21

la réponse simple est que vous ne pourrez pas avoir la struct implémenter votre interface en ayant SetSomeField travaillez comme vous voulez.

cependant, un pointeur vers la structure implémentera l'interface, donc changer votre Create méthode pour le faire return &obj devrait faire fonctionner les choses.

Le problème sous-jacent est que votre modifiée SetSomeField méthode n'est plus dans la méthode de Implementation. Alors que le type *Implementation héritera des méthodes de récepteur sans pointeur, le inverse n'est pas vrai.

la raison en est liée à la façon dont les variables d'interface sont spécifiées: la seule façon d'accéder à la valeur dynamique stockée dans une variable d'interface est de la copier. Par exemple, imaginez la suivante:

var impl Implementation
var iface IFace = &impl

Dans ce cas, un appel à iface.SetSomeField fonctionne, car il peut copier le pointeur à utiliser comme récepteur de l'appel de méthode. Si on stockait directement une struct dans la variable d'interface, il faudrait créer un pointeur vers cette struct pour la compléter. l'appel de la méthode. Une fois qu'un tel pointeur est créé, il est possible d'accéder (et éventuellement de modifier) à la valeur dynamique de la variable d'interface sans la copier.

12
répondu James Henstridge 2015-11-26 11:11:09