Cette erreur de compilation se produit lorsque vous essayez d'affecter ou de passer (ou de convertir) un type concret à un type d'interface; et le type lui-même n'implémente pas l'interface, seulement un pointeur vers le type .
Voyons un exemple:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Le Stringer
type d'interface a une seule méthode: String()
. Toute valeur stockée dans une valeur d'interface Stringer
doit avoir cette méthode. Nous avons également créé un MyType
, et nous avons créé une méthode MyType.String()
avec récepteur de pointeur . Cela signifie que la String()
méthode se trouve dans l' ensemble de méthodes du *MyType
type, mais pas dans celui de MyType
.
Lorsque nous essayons d'attribuer une valeur de MyType
à une variable de type Stringer
, nous obtenons l'erreur en question:
m := MyType{value: "something"}
var s Stringer
s = m // cannot use m (type MyType) as type Stringer in assignment:
// MyType does not implement Stringer (String method has pointer receiver)
Mais tout va bien si nous essayons d'attribuer une valeur de type *MyType
à Stringer
:
s = &m
fmt.Println(s)
Et nous obtenons le résultat attendu (essayez-le sur le Go Playground ):
something
Donc, les exigences pour obtenir cette erreur de compilation:
- Une valeur de type concret non pointeur étant affectée (ou passée ou convertie)
- Un type d'interface affecté à (ou transmis à, ou converti en)
- Le type concret a la méthode requise de l'interface, mais avec un récepteur de pointeur
Possibilités de résoudre le problème:
- Un pointeur vers la valeur doit être utilisé, dont l'ensemble de méthodes inclura la méthode avec le récepteur de pointeur
- Ou le type de récepteur doit être changé en non-pointeur , donc l'ensemble de méthodes du type concret non-pointeur contiendra également la méthode (et satisfera ainsi l'interface). Cela peut être viable ou non, car si la méthode doit modifier la valeur, un récepteur sans pointeur n'est pas une option.
Structures et encastrement
Lorsque vous utilisez des structures et l'incorporation , ce n'est souvent pas «vous» qui implémentez une interface (fournissez une implémentation de méthode), mais un type que vous intégrez dans votre struct
. Comme dans cet exemple:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
Encore une fois, erreur au moment de la compilation, car l'ensemble de méthodes de MyType2
ne contient pas la String()
méthode de l'incorporé MyType
, uniquement l'ensemble de méthodes de *MyType2
, donc les opérations suivantes fonctionnent (essayez-les sur le Go Playground ):
var s Stringer
s = &m2
Nous pouvons également le faire fonctionner, si nous incorporons *MyType
et utilisons uniquement un non-pointeur MyType2
(essayez-le sur le Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
De plus, quoi que nous incorporions (soit MyType
ou *MyType
), si nous utilisons un pointeur *MyType2
, cela fonctionnera toujours (essayez-le sur le Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
Section pertinente de la spécification (de la section Types de structures ):
Étant donné un type de structure S
et un type nommé T
, les méthodes promues sont incluses dans l'ensemble de méthodes de la structure comme suit:
- Si
S
contient un champ anonyme T
, les ensembles de méthodes de S
et les *S
deux incluent des méthodes promues avec le récepteur T
. L'ensemble de *S
méthodes comprend également des méthodes promues avec récepteur *T
.
- Si
S
contient un champ anonyme *T
, les ensembles de méthodes de S
et les *S
deux incluent des méthodes promues avec le récepteur T
ou *T
.
En d'autres termes: si nous incorporons un type sans pointeur, l'ensemble de méthodes de l'embedder sans pointeur obtient uniquement les méthodes avec des récepteurs sans pointeur (du type incorporé).
Si nous incorporons un type pointeur, l'ensemble de méthodes de l'embedder non pointeur obtient des méthodes avec des récepteurs pointeurs et non pointeurs (du type incorporé).
Si nous utilisons une valeur de pointeur vers l'embedder, que le type incorporé soit pointeur ou non, l'ensemble de méthodes du pointeur vers l'embedder obtient toujours des méthodes avec les récepteurs pointeurs et non pointeurs (du type embarqué).
Remarque:
Il existe un cas très similaire, à savoir lorsque vous avez une valeur d'interface qui encapsule une valeur de MyType
, et que vous essayez de taper assert une autre valeur d'interface à partir de celle-ci Stringer
,. Dans ce cas, l'assertion ne tiendra pas pour les raisons décrites ci-dessus, mais nous obtenons une erreur d'exécution légèrement différente:
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
Panique à l'exécution (essayez-le sur le Go Playground ):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Tentative de conversion au lieu de type assert, nous obtenons l'erreur de compilation dont nous parlons:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
func (m *MyType)
", ou aucun . En est-il ainsi? Puis-je mélanger différents types de "fonctions membres", par exemple,func (m *MyType)
&func (m MyType)
?MyType
, si vous ne pouvez pas changerMyType
pour utiliser des méthodes de valeur. J'ai essayé quelque chose comme ça(&i).(*Stringer)
mais ça ne fonctionne pas. Est-ce même possible?x := i.(MyType)
, puis vous pouvez appeler des méthodes avec récepteur de pointeur dessus, par exemplei.String()
, qui est un raccourci(&i).String()
qui réussit car les variables sont adressables. Mais la méthode du pointeur modifiant la valeur (la valeur pointée) ne sera pas reflétée dans la valeur enveloppée dans la valeur d'interface, c'est pourquoi cela n'a pas de sens.*T
ne sont pas incluses dans l'ensemble de méthodesS
car ellesS
peuvent ne pas être adressables (par exemple, la valeur de retour de la fonction ou le résultat de l'indexation de la carte), et aussi parce que souvent seule une copie est présente / reçue, et si la prise de son adresse est autorisée, la méthode avec le récepteur de pointeur ne pouvait que modifier la copie (confusion car vous supposeriez que l'original est modifié). Voir cette réponse pour un exemple: Utilisation de SetString de réflexion .Pour être bref, disons que vous avez ce code et que vous avez une interface Loader et un WebLoader qui implémente cette interface.
Donc, ce code vous donnera cette erreur de temps de compilation
Donc, ce que vous avez seulement à faire est de changer
webLoader := WebLoader{}
comme suit:Alors pourquoi cela va se corriger car vous définissez cette fonction
func (w *WebLoader) Load
pour accepter un récepteur de pointeur. Pour plus d'explications, veuillez lire les réponses @icza et @karorala source
Un autre cas où j'ai vu ce genre de chose se produire, c'est si je veux créer une interface où certaines méthodes modifieront une valeur interne et d'autres pas.
Quelque chose qui implémente alors cette interface pourrait être comme:
Donc, le type d'implémentation aura probablement des méthodes qui sont des récepteurs de pointeurs et d'autres qui ne le sont pas et puisque j'ai une variété de ces différentes choses qui sont des GetterSetters, je voudrais vérifier dans mes tests qu'ils font tous ce qui est attendu.
Si je devais faire quelque chose comme ça:
Alors je ne vais pas obtenir ce qui précède « X ne met pas en oeuvre Y (méthode Z a récepteur pointeur) » erreur (car il est une erreur de compilation) mais je le ferai avoir une mauvaise journée pourchassant exactement pourquoi mon test échoue .. .
Au lieu de cela, je dois m'assurer de faire la vérification de type à l'aide d'un pointeur, tel que:
Ou:
Alors tout le monde est content des tests!
Mais attendez! Dans mon code, j'ai peut-être des méthodes qui acceptent un GetterSetter quelque part:
Si j'appelle ces méthodes depuis une autre méthode de type, cela générera l'erreur:
L'un des appels suivants fonctionnera:
la source